Perché non sto ottenendo una java.util.ConcurrentModificationException in questo esempio?

Nota: sono a conoscenza del metodo Iterator#remove() .

Nel seguente esempio di codice, non capisco perché List.remove nel metodo main lanci ConcurrentModificationException , ma non nel metodo remove .

 public class RemoveListElementDemo { private static final List integerList; static { integerList = new ArrayList(); integerList.add(1); integerList.add(2); integerList.add(3); } public static void remove(Integer toRemove) { for(Integer integer : integerList) { if(integer.equals(toRemove)) { integerList.remove(integer); } } } public static void main(String... args) { remove(Integer.valueOf(2)); Integer toRemove = Integer.valueOf(3); for(Integer integer : integerList) { if(integer.equals(toRemove)) { integerList.remove(integer); } } } } 

Ecco perché: come dice il Javadoc:

Gli iteratori restituiti dall’iter iteratore di questa class e i metodi listIterator sono veloci: se la lista viene modificata strutturalmente in qualsiasi momento dopo la creazione dell’iteratore, in qualsiasi modo tranne che attraverso i metodi di rimozione o aggiunta dell’iteratore, l’iteratore genererà una ConcurrentModificationException.

Questo controllo viene eseguito nel metodo next() dell’iteratore (come puoi vedere dallo stacktrace). Ma raggiungeremo il metodo next() solo se hasNext() consegnato true, che è ciò che viene chiamato da ciascuno per verificare se il limite è soddisfatto. Nel tuo metodo di rimozione, quando hasNext() verifica se è necessario restituire un altro elemento, vedrà che ha restituito due elementi e ora, dopo che un elemento è stato rimosso, l’elenco contiene solo due elementi. Quindi tutto è peachy e abbiamo finito con l’iterazione. Il controllo delle modifiche concomitanti non si verifica, come avviene nel metodo next() che non viene mai chiamato.

Quindi arriviamo al secondo ciclo. Dopo aver rimosso il secondo numero, il metodo hasNext verificherà di nuovo se può restituire più valori. Ha già restituito due valori, ma l’elenco ora ne contiene solo uno. Ma il codice qui è:

 public boolean hasNext() { return cursor != size(); } 

1! = 2, quindi continuiamo con il metodo next() , che ora si rende conto che qualcuno ha giocato con l’elenco e fa scattare l’eccezione.

Spero che chiarisca la tua domanda.

Sommario

List.remove() non genererà ConcurrentModificationException quando rimuove il penultimo elemento dall’elenco.

Un modo per gestirlo per rimuovere qualcosa da una copia di una Collection (non dalla Collezione stessa), se applicabile. Clone la raccolta originale per fare una copia tramite un Constructor .

Questa eccezione può essere generata da metodi che hanno rilevato la modifica simultanea di un object quando tale modifica non è consentita.

Per il tuo caso specifico, prima di tutto, non penso che la final sia una strada da percorrere considerando che intendi modificare la lista delle dichiarazioni precedenti

 private static final List integerList; 

Considera anche la possibilità di modificare una copia anziché l’elenco originale.

 List copy = new ArrayList(integerList); for(Integer integer : integerList) { if(integer.equals(remove)) { copy.remove(integer); } } 

Il metodo forward / iterator non funziona quando si rimuovono gli oggetti. È ansible rimuovere l’elemento senza errori, ma si otterrà un errore di runtime quando si tenta di accedere agli elementi rimossi. Non è ansible utilizzare l’iteratore perché, poiché pushy mostra che causerà una ConcurrentModificationException, quindi utilizzare un ciclo for regolare, ma fare un passo indietro attraverso di esso.

 List integerList; integerList = new ArrayList(); integerList.add(1); integerList.add(2); integerList.add(3); int size= integerList.size(); //Item to remove Integer remove = Integer.valueOf(3); 

Una soluzione:

Attraversare la matrice in ordine inverso se si intende rimuovere un elemento della lista. Semplicemente andando all’indietro attraverso l’elenco eviti di visitare un object che è stato rimosso, eliminando l’eccezione.

 //To remove items from the list, start from the end and go backwards through the arrayList //This way if we remove one from the beginning as we go through, then we will avoid getting a runtime error //for java.lang.IndexOutOfBoundsException or java.util.ConcurrentModificationException as when we used the iterator for (int i=size-1; i> -1; i--) { if (integerList.get(i).equals(remove) ) { integerList.remove(i); } } 

Questo snippet genererà sempre una ConcurrentModificationException.

La regola è “Non puoi modificare (aggiungere o rimuovere elementi dall’elenco) mentre esegui un’iterazione su di esso usando un Iterator (che si verifica quando usi un ciclo for-each)”.

JavaDocs:

Gli iteratori restituiti dall’iter iteratore di questa class e i metodi listIterator sono veloci: se la lista viene modificata strutturalmente in qualsiasi momento dopo la creazione dell’iteratore, in qualsiasi modo tranne che attraverso i metodi di rimozione o aggiunta dell’iteratore, l’iteratore genererà una ConcurrentModificationException.

Quindi, se si desidera modificare l’elenco (o qualsiasi raccolta in generale), utilizzare iteratore, poiché in tal caso è a conoscenza delle modifiche e quindi verranno gestite correttamente.

Spero che questo ti aiuti.

Ho avuto lo stesso problema, ma nel caso in cui stavo aggiungendo en elemento nella lista iterata. L’ho fatto in questo modo

 public static void remove(Integer remove) { for(int i=0; i 

Ora tutto va bene perché non si crea alcun iteratore sulla lista, si itera su di essa "manualmente". E la condizione i < integerList.size() non ti i < integerList.size() mai perché quando rimuovi / aggiungi qualcosa nella dimensione Elenco del decremento / incremento della lista ..

Spero che aiuti, per me quella era una soluzione.

Funziona bene su Java 1.6

~% javac RemoveListElementDemo.java
~% java RemoveListElementDemo
~% cat RemoveListElementDemo.java

 import java.util.*; public class RemoveListElementDemo { private static final List integerList; static { integerList = new ArrayList(); integerList.add(1); integerList.add(2); integerList.add(3); } public static void remove(Integer remove) { for(Integer integer : integerList) { if(integer.equals(remove)) { integerList.remove(integer); } } } public static void main(String... args) { remove(Integer.valueOf(2)); Integer remove = Integer.valueOf(3); for(Integer integer : integerList) { if(integer.equals(remove)) { integerList.remove(integer); } } } } 

~%

Se si utilizzano raccolte di copia su scrittura, funzionerà; tuttavia, quando si utilizza list.iterator (), Iterator restituito farà sempre riferimento alla raccolta di elementi così com’era quando (come sotto) è stato chiamato list.iterator (), anche se un altro thread modifica la raccolta. Qualsiasi metodo di muting chiamato su Iterator o ListIterator basato su copy-on-write (come aggiungere, impostare o rimuovere) genererà una UnsupportedOperationException.

 import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; public class RemoveListElementDemo { private static final List integerList; static { integerList = new CopyOnWriteArrayList<>(); integerList.add(1); integerList.add(2); integerList.add(3); } public static void remove(Integer remove) { for(Integer integer : integerList) { if(integer.equals(remove)) { integerList.remove(integer); } } } public static void main(String... args) { remove(Integer.valueOf(2)); Integer remove = Integer.valueOf(3); for(Integer integer : integerList) { if(integer.equals(remove)) { integerList.remove(integer); } } } } 

Cambia Iterator for each for loop da risolvere.

E la ragione è:

Gli iteratori restituiti dall’iter iteratore di questa class e i metodi listIterator sono veloci: se la lista viene modificata strutturalmente in qualsiasi momento dopo la creazione dell’iteratore, in qualsiasi modo tranne che attraverso i metodi di rimozione o aggiunta dell’iteratore, l’iteratore genererà una ConcurrentModificationException.

– Documento Java rinviato.

Nel mio caso l’ho fatto in questo modo:

 int cursor = 0; do { if (integer.equals(remove)) integerList.remove(cursor); else cursor++; } while (cursor != integerList.size()); 

Controlla il tuo codice uomo ….

Nel metodo principale si sta tentando di rimuovere il 4 ° elemento che non c’è e quindi l’errore. Nel metodo remove () si sta tentando di rimuovere il terzo elemento che è lì e quindi nessun errore.