Vorrei fare alcuni esempi con alcune alternative per evitare a ConcurrentModificationException
.
Supponiamo di avere la seguente raccolta di libri
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")));
Raccogli e rimuovi
La prima tecnica consiste nel raccogliere tutti gli oggetti che vogliamo eliminare (ad es. Usando un ciclo for avanzato) e dopo aver finito l'iterazione, rimuoviamo tutti gli oggetti 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);
Ciò suppone che l'operazione che si desidera eseguire sia "elimina".
Se si desidera "aggiungere" anche questo approccio funzionerebbe, ma suppongo che si ripeterà una raccolta diversa per determinare quali elementi si desidera aggiungere a una seconda raccolta e quindi emettere un addAll
metodo alla fine.
Utilizzando ListIterator
Se si lavora con elenchi, un'altra tecnica consiste nell'utilizzare un ListIterator
supporto per la rimozione e l'aggiunta di elementi durante l'iterazione stessa.
ListIterator<Book> iter = books.listIterator();
while(iter.hasNext()){
if(iter.next().getIsbn().equals(isbn)){
iter.remove();
}
}
Ancora una volta, ho usato il metodo "rimuovi" nell'esempio sopra, che è quello che la tua domanda sembra implicare, ma puoi anche usare il suo add
metodo per aggiungere nuovi elementi durante l'iterazione.
Utilizzando JDK> = 8
Per coloro che lavorano con Java 8 o versioni superiori, ci sono un paio di altre tecniche che è possibile utilizzare per trarne vantaggio.
È possibile utilizzare il nuovo removeIf
metodo nella Collection
classe base:
ISBN other = new ISBN("0-201-63361-2");
books.removeIf(b -> b.getIsbn().equals(other));
Oppure usa la nuova API stream:
ISBN other = new ISBN("0-201-63361-2");
List<Book> filtered = books.stream()
.filter(b -> b.getIsbn().equals(other))
.collect(Collectors.toList());
In quest'ultimo caso, per filtrare gli elementi fuori da una raccolta, è necessario riassegnare il riferimento originale alla raccolta filtrata (ad esempio books = filtered
) o utilizzare la raccolta filtrata per removeAll
gli elementi trovati della raccolta originale (ad es.books.removeAll(filtered)
.).
Usa sublista o sottoinsieme
Ci sono anche altre alternative. Se l'elenco è ordinato e si desidera rimuovere elementi consecutivi, è possibile creare un elenco secondario e quindi cancellarlo:
books.subList(0,5).clear();
Poiché l'elenco secondario è supportato dall'elenco originale, questo sarebbe un modo efficace per rimuovere questa raccolta secondaria di elementi.
Qualcosa di simile potrebbe essere ottenuto con insiemi ordinati usando il NavigableSet.subSet
metodo o uno qualsiasi dei metodi di slicing offerti lì.
considerazioni:
Quale metodo usi potrebbe dipendere da ciò che intendi fare
- Il colleziona e
removeAl
tecnica funzionano con qualsiasi raccolta (raccolta, elenco, set, ecc.).
- La
ListIterator
tecnica ovviamente funziona solo con gli elenchi, a condizione che la loro ListIterator
implementazione fornita offra supporto per le operazioni di aggiunta e rimozione.
- L'
Iterator
approccio funzionerebbe con qualsiasi tipo di raccolta, ma supporta solo le operazioni di rimozione.
- Con l' approccio
ListIterator
/ Iterator
l'ovvio vantaggio non è di dover copiare nulla poiché rimuoviamo mentre ripetiamo. Quindi, questo è molto efficiente.
- L'esempio dei flussi JDK 8 in realtà non ha rimosso nulla, ma ha cercato gli elementi desiderati, quindi abbiamo sostituito il riferimento della raccolta originale con quello nuovo e abbiamo lasciato che il vecchio venisse raccolto. Quindi, ripetiamo solo una volta la raccolta e sarebbe efficace.
- Nella raccolta e
removeAll
approccio lo svantaggio è che dobbiamo iterare due volte. Per prima cosa iteriamo nel ciclo di ricerca alla ricerca di un oggetto che soddisfi i nostri criteri di rimozione e, una volta trovato, chiediamo di rimuoverlo dalla raccolta originale, il che implicherebbe un secondo lavoro di iterazione per cercare questo oggetto al fine di rimuoverla.
- Penso che valga la pena ricordare che il metodo di rimozione
Iterator
dell'interfaccia è contrassegnato come "opzionale" in Javadocs, il che significa che potrebbero esserci Iterator
implementazioni che possono essere lanciate UnsupportedOperationException
se invochiamo il metodo di rimozione. In quanto tale, direi che questo approccio è meno sicuro di altri se non possiamo garantire il supporto dell'iteratore per la rimozione di elementi.