2012-04-01 24 views
5

ho avuto un problema con l'eliminazione di un oggetto da ArrayList quando si lavora sulla cessione Se uso il "normale" per il ciclo, funziona come segueRimozione ArrayList questione oggetto

public void returnBook(String isbn){   
    for (int i = 0; i < booksBorrowed.size(); i++){    
     if (booksBorrowed.get(i).getISBN() == isbn){ 
      booksBorrowed.get(i).returnBook(); 
      booksBorrowed.remove(i);     
     } 
    } 
} 

Tuttavia, quando sono cercando di semplificare il codice con una maggiore ciclo for, che non funziona e che mostra java.util.ConcurrentModificationException errore:

public void returnBook(String isbn){   
     for (Book book: booksBorrowed){    
      if (book.getISBN() == isbn){ 
       book.returnBook(); 
       booksBorrowed.remove(book);     
      } 
     } 
} 

Spero che voi ragazzi potrebbe alleggerire me up ..

+0

Se la tua domanda è "Perché ricevo un errore" è perché non è possibile rimuovere elementi da un elenco su cui si sta iterando. E il tuo primo ciclo potrebbe avere un bug in esso, se lo stesso codice ISBN può essere nella lista due volte. –

risposta

7

Le alternative per evitare un ConcurrentModificationException sono:

List<Book> books = new ArrayList<Book>(); 
books.add(new Book(new ISBN("0-201-63361-2"))); 
books.add(new Book(new ISBN("0-201-63361-3"))); 
books.add(new Book(new ISBN("0-201-63361-4"))); 

Raccogliere tutti i record che si desidera eliminare il maggiore ciclo for, e dopo aver terminato l'iterazione , rimuovi tutti i record trovati.

ISBN isbn = new ISBN("0-201-63361-2"); 
List<Book> found = new ArrayList<Book>(); 
for(Book book : books){ 
    if(book.getIsbn().equals(isbn)){ 
     found.add(book); 
    } 
} 
books.removeAll(found); 

Oppure si può utilizzare un ListIterator, che ha il supporto per un metodo di rimozione durante l'iterazione stessa.

ListIterator<Book> iter = books.listIterator(); 
while(iter.hasNext()){ 
    if(iter.next().getIsbn().equals(isbn)){ 
     iter.remove(); 
    } 
} 

Oppure si può utilizzare una libreria di terze parti come LambdaJ e rende tutto il lavoro per voi dietro le quinte>

List<Book> filtered = select(books, 
       having(on(Book.class).getIsbn(), 
         is(new ISBN("0-201-63361-2")))); 
+0

Grazie amico, Problema risolto :) – babygau

+0

Mi hai salvato la vita. Grazie –

4

In realtà non dovresti farlo perché potrebbero causare problemi alla fine. Usa invece l'iteratore di ArrayList per aiutarti a scorrere l'elenco e quindi rimuovi solo con l'iteratore. Questo aiuterà a prevenire errori di modifica simultanea perniciosa.

+1

/golfclap uso di 'pernicioso' –

0

Quando si utilizza il ciclo avanzato in Java, viene utilizzato l'Iterator dell'elenco per scorrere l'elenco. Quando si rimuove un oggetto con la funzione di rimozione dell'elenco, questo interferirà con lo stato dell'iteratore e l'iteratore genererà una ConcurrentModificationException. Con il ciclo for semplice non hai questo problema perché stai usando la lista e il cambio di stato avviene solo nella lista stessa.

+0

Cura di illuminarmi un po 'con come utilizzare l'iteratore per rimuovere l'oggetto Book – babygau

+0

dipende davvero da cosa sei un'applicazione e che tipo di prestazioni vuoi. Lo farà una combinazione di hashmap e arraylist iterator. – amshali

1

Hai un bug nel codice:

for (int i = 0; i < booksBorrowed.size(); i++){    
    if (booksBorrowed.get(i).getISBN() == isbn){ 
     booksBorrowed.get(i).returnBook(); 
     booksBorrowed.remove(i);     
    } 
} 

salta prossimi elementi dopo quelli rimossi. Per esempio. quando hai rimosso l'elemento "0th", 1st diventa 0th, ma questo codice non scorre iterandolo.

Questa è una versione corretta:

for (int i = booksBorrowed.size() - 1; i >= 0; i--){    
    if (booksBorrowed.get(i).getISBN() == isbn){ 
     booksBorrowed.get(i).returnBook(); 
     booksBorrowed.remove(i);     
    } 
} 

Ma questo non è l'approccio migliore, perché è la complessità è O (n^2).

Uno migliore è quello di aggiungere tutti gli elementi conservati a un'altra raccolta e quindi copiarli nuovamente all'elenco originale con le dimensioni troncate. La complessità è O (n). Certo, è una preoccupazione solo se ci sono molti elementi da rimuovere.

P.S. rimuovendo in una costruzione per ogni interruzione iteratore, quindi non è un modo valido per elaborare l'elenco in questo caso.

Ma è possibile effettuare le seguenti operazioni:

for (Iterator<String> i = a.iterator(); i.hasNext();) { 
     Book next = i.next(); 
     if (book.getISBN() == isbn){ 
      book.returnBook(); 
      i.remove(i);     
     } 
    } 

Anche in questo caso, la complessità è O (n^2) in questo caso.

+0

Primo ciclo funziona se aggiunge il super-lordo "i--;" in fondo alla dichiarazione if. –

+0

cosa intendi? –

+0

Nell'ultimo, rimuove da ArrayList o solo dall'iteratore? –

2

Tutte buone risposte. Ma vorrei suggerirti di ripensarlo. Voglio dire, hai davvero bisogno di un ArrayList o di un HashMap sarebbe meglio? Se la tua lista di oggetti ha una chiave unic (ISBN), e la usi per ottenere ogni oggetto, perché non usare una collezione appropriata per il tuo problema?

È Woud farlo solo

public void returnBook(String isbn){   
    Book book = (Book) booksBorrowed.remove(isbn);    
    book.returnBook();  
}