Eccezione di modifica simultanea

Ho questo piccolo pezzo di codice e mi dà l’eccezione di modifica simultanea. Non riesco a capire perché continuo a ottenerlo, anche se non vedo nessuna modifica concomitante eseguita.

import java.util.*; public class SomeClass { public static void main(String[] args) { List s = new ArrayList(); ListIterator it = s.listIterator(); for (String a : args) s.add(a); if (it.hasNext()) String item = it.next(); System.out.println(s); } } 

Per evitare ConcurrentModificationException , dovresti scrivere il tuo codice in questo modo:

 import java.util.*; public class SomeClass { public static void main(String[] args) { List s = new ArrayList(); for(String a : args) s.add(a); ListIterator it = s.listIterator(); if(it.hasNext()) { String item = it.next(); } System.out.println(s); } } 

Un java.util.ListIterator consente di modificare un elenco durante l’iterazione, ma non tra la sua creazione e il suo utilizzo.

Non riesco a capire perché continuo a ottenerlo, anche se non vedo nessuna modifica concomitante eseguita.

Tra la creazione dell’iteratore e l’avvio dell’iterazione, hai aggiunto argomenti all’elenco che deve essere iterato. Questa è una modifica simultanea.

  ListIterator it = s.listIterator(); for (String a : args) s.add(a); // concurrent modification here if (it.hasNext()) String item = it.next(); // exception thrown here 

Crea l’iteratore DOPO aver completato l’aggiunta di elementi all’elenco:

  for (String a : args) s.add(a); ListIterator it = s.listIterator(); if (it.hasNext()) String item = it.next(); 

Da JavaDoc: for ConcurrentModificatoinException: “generalmente non è ansible per un thread modificare un insieme mentre un altro thread sta iterando su di esso”.

Significa semplicemente che se hai ancora un iteratore aperto, non ti è permesso modificare l’elenco perché il ciclo iteratore si interromperà. Prova a spostare ListIterator it = s.listIterator(); fino a dopo il ciclo for.

Non è consentito continuare la iterazione su un iteratore dopo la modifica dell’elenco sottostante. Qui crei l’iteratore prima di aggiungere alcuni elementi a s , quindi procedi a eseguire hasNext() e a next() su di esso dopo le aggiunte, portando a ConcurrentModificationException

Se le soluzioni di cui sopra non funziona correttamente. Puoi usare old for-loop per iterare una lista allo stesso tempo aggiungendo nuovi elementi. Vedi l’esempio qui sotto:

 import java.util.*; public class SomeClass { public static void main(String[] args) { ArrayList aList = new ArrayList(); // we will iterate this // this will cause ConcurrentModificationException. // Since we are iterating the list, at the same time modifying it. /*for(AClass a: aList){ aList.add(someMethod(a)); }*/ // old fashion for-loop will help int limit = aList.size(); for(int i=0; ctr 

ConcurrentModificationException può sorgere sia in ambiente con singolo thread che in ambiente multi-thread . Il problema principale è che tutti gli iteratori generici (come quello usato in ArrayList) sono tutti iteratori FailFast , che falliscono quando proviamo a modificare un elenco se un iteratore sta già iterando su di esso. Soluzione -> Utilizzare CopyOnWriteArrayList se tale scenario è richiesto dal requisito anziché utilizzare ArrayList.

Per una dimostrazione completa di questo, è ansible utilizzare il codice riportato di seguito. Abbiamo solo bisogno di cambiare l’implementazione da CopyOnWriteArrayList a ArrayList.

 import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; /** * @author narif * */ public class TestApp { /** * @param args */ public static void main(String[] args) { List testList = new ArrayList<>(); testList.add("abc"); testList.add("abc"); testList.add("abc"); testList.add("abc"); testList.add("abc"); testList.add("abc"); testList.add("abc"); testList.add("abc"); testList.add("abc"); testList.add("abc"); testList.add("abc"); testList.add("abc"); testList.add(6, "abcAtindex6"); int size = testList.size(); System.out.println("The Current List (ArrayList) is: " + testList); System.out.println("The size of the List (ArrayList) is: " + size); /* Comment the below lines to get the ConcurrentModificationException */ testList = new CopyOnWriteArrayList<>(testList); for (String value : testList) { System.out.println("The Value from ForEach Loop is: " + value); /* * Concurrent modification is happening here * One iterator is iterating over the list while we are trying to add new values to * the list so the results of the iteration are undefined under these circumstances. * So teh fail fast iterators will fail and will throw the ConcurrentModificationException. */ testList.add("valueFromForLoop"); testList.add("anotherValueFromForEachLoop"); } Iterator it = testList.iterator(); while (it.hasNext()) { String abc = it.next(); System.out.println(abc); testList.add("Value from Iterator1"); testList.add("Value from Iterator2"); testList.add("Value from Iterator3"); testList.add("Value from Iterator4"); } System.out.println("Did the modificationa and all after conevrting the ArrayList to CopyOnWriteArrayList."); System.out.println("Calling the method to get the new List.."); testList = new CopyOnWriteArrayList<>(getTheList(testList)); for (String value : testList) { System.out.println("The value returned from method is : " + value); } } private static List getTheList(List pList) { List list = new CopyOnWriteArrayList<>(pList); int i = 0; for (String lValue : list) { System.out.println("The list Passed is " + list); i++; list.add("localVaueFromMethod" + i); list.removeAll(pList); } return list; } } 

Per ulteriori informazioni su questo link, questo potrebbe essere utile su ConcurrentModificationException Java Docs

Dai un’occhiata alla pagina della documentazione di Oracle.

 public class ConcurrentModificationException extends RuntimeException 

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

Si noti che questa eccezione non indica sempre che un object è stato modificato contemporaneamente da un thread diverso. Se un singolo thread genera una sequenza di invocazioni di metodi che violano il contratto di un object, l’object può lanciare questa eccezione. Ad esempio, se un thread modifica direttamente una raccolta mentre itera sulla raccolta con un iteratore fail-fast, l’iteratore genererà questa eccezione .

Nel tuo caso, hai modificato la collezione dopo aver creato l’iteratore e quindi hai riscontrato l’eccezione.

Se cambi il tuo codice come da risposta Stephen C , non otterrai questo errore.

Questo non ha funzionato:

 LinkedList linkedList = new LinkedList(); ListIterator listIterator = linkedList.listIterator(); linkedList.add("aa"); linkedList.add("bb"); 

Questo ha funzionato:

 LinkedList linkedList = new LinkedList(); linkedList.add("aa"); linkedList.add("bb"); ListIterator listIterator = linkedList.listIterator(); 

per capire questo si può dare un’occhiata all’origine dell’implementazione di HashMap:

 public class HashMap extends AbstractMap implements Cloneable, Serializable{ 

che contiene HashIterator come di seguito:

 private abstract class HashIterator { ... int expectedModCount = modCount; ... HashMapEntry nextEntry() { if (modCount != expectedModCount) throw new ConcurrentModificationException(); .... } 

ogni volta che crei un iteratore:

  • un contatore previstoModCount viene creato ed è impostato sul valore di modCount come checkpoint di ingresso
  • modCount viene incrementato in caso di utilizzo put / get (aggiungi / rimuovi)
  • nextEntry method of iterator sta controllando questo valore con modCount corrente se sono diverse le eccezioni di modifica simultanea è throw

per evitare questo puoi:

  • convertire la mappa in un array (non consigliato per mappe di grandi dimensioni)
  • usa la mappa della concorrenza o le classi della lista ( CopyOnWriteArrayList / ConcurrentMap )
  • mappa di blocco (questo approccio rimuove i vantaggi del multithreading)

questo ti permetterà di iterare e aggiungere o rimuovere elementi allo stesso tempo senza aumentare un’eccezione

La mappa di concorrenza / elenco iteratore è un iteratore “debolmente coerente” che non genererà ConcurrentModificationException e garantisce di attraversare gli elementi così come sono esistiti dopo la costruzione dell’iteratore e potrebbe (ma non è garantito) riflettere eventuali modifiche successive alla costruzione.

Maggiori informazioni su CopyOnWriteArrayList