ConcurrentModificationException quando si aggiunge all’interno di un ciclo foreach in ArrayList

Sto cercando di utilizzare il ciclo foreach con l’arraylist, ma quando lo uso, mi dà errore, ma quando uso il ciclo normale, funziona perfettamente, quale potrebbe essere il problema?

Il codice è qui:

for (Pair p2 : R) { if ((p2.getFirstElm() == p.getSecondElm()) && (p2.getFirstElm() != p2.getSecondElm())) R.add(new Pair (p.getFirstElm(), p2.getSecondElm())); else if ((p2.getSecondElm() == p.getFirstElm()) && (p2.getFirstElm() != p2.getSecondElm())) R.add(new Pair (p2.getFirstElm(), p.getSecondElm())); // else // There are no transitive pairs in R. } 

questo è il ciclo che non funziona, ed ecco quello che funziona:

 for (int i = 0; i < R.size(); i++) { if ((R.get(i).getFirstElm() == p.getSecondElm()) && (R.get(i).getFirstElm() != R.get(i).getSecondElm())) R.add(new Pair (p.getFirstElm(), R.get(i).getSecondElm())); else if ((R.get(i).getSecondElm() == p.getFirstElm()) && (R.get(i).getFirstElm() != R.get(i).getSecondElm())) R.add(new Pair (R.get(i).getFirstElm(), p.getSecondElm())); //else // There are no transitive pairs in R. } 

l’errore che sto ricevendo quando si utilizza il ciclo foreach è:

 Exception in thread "main" java.util.ConcurrentModificationException at java.util.AbstractList$Itr.checkForComodification(Unknown Source) at java.util.AbstractList$Itr.next(Unknown Source) at set.problem.fourth.PoSet.makeTransitive(PoSet.java:145) at set.problem.fourth.PoSet.addToR(PoSet.java:87) at set.problem.fourth.PoSetDriver.typicalTesting(PoSetDriver.java:35) at set.problem.fourth.PoSetDriver.main(PoSetDriver.java:13) 

Le classi Java Collection sono fail-veloci, il che significa che se la Collection verrà modificata mentre alcuni thread lo attraversano usando iterator, iterator.next() genererà una ConcurrentModificationException .

Questa situazione può verificarsi in caso di ambiente multithread e single-threaded. – http://www.javacodegeeks.com

Non è ansible modificare un List in un ciclo for/each , che è lo zucchero sintattico attorno a Iterator come dettaglio di implementazione. È ansible chiamare tranquillamente .remove() quando si utilizza direttamente l’ Iterator .

Notare che Iterator.remove è l’unico modo sicuro per modificare una raccolta durante l’iterazione; il comportamento non è specificato se la raccolta sottostante viene modificata in qualsiasi altro modo mentre è in corso l’iterazione. – Tutorial Collezioni Java

Chiamare .add() all’interno del ciclo for/each modifica il contenuto, e l’ Iterator che viene utilizzato dietro le quinte vede questo e lancia questa eccezione.

Una preoccupazione più sottile è che il secondo modo in cui si elenca, il .size() sta aumentando ogni volta che si .add() così si finirà per elaborare tutte le cose .add() , questo potrebbe causare un loop infinito a seconda su quali sono i dati di input. Non sono sicuro se questo è ciò che desideri.

Soluzione

Vorrei creare un altro ArrayList e .add() con tutte le cose nuove, e poi dopo il ciclo, usare .addAll() sul ArrayList originale per combinare insieme i due elenchi. Ciò renderà le cose esplicite in ciò che stai cercando di fare, a meno che la tua intenzione non elabori tutte le cose appena aggiunte man mano che le aggiungi.

Soluzione 2014:

Utilizza sempre classi di collezioni Immutable e crea nuove classi di raccolta Immutable invece di provare a modificare una singola condivisa. Questo è fondamentalmente ciò che dice la mia risposta del 2012, ma volevo renderlo più esplicito.

Guava lo supporta molto bene, usa ImmutableList.copyOf() per passare i dati.

Usa Iterables.filter() per filtrare le cose in una nuova ImmutableList , nessuno stato mutabile condiviso, nessun problema di concorrenza!

Sotto il cofano, il ciclo for-each in Java utilizza un Iterator per attraversare la collezione (vedere questo articolo per una spiegazione dettagliata). E l’Iterator genererà una ConcurrentModificationException se modifichi la raccolta mentre la itera su di essa, vedi questo post .

Il problema è che stai facendo il R.add () nella prima riga del ciclo.

Nella prima situazione hai un iteratore aperto all’arrayist. Quando fai un add e poi tenti di ripetere l’iteratore nota che la struttura dei dati è cambiata sotto di te.

Nel caso del look for hai solo un nuovo elemento ogni volta e non hai il problema di modifica simultanea, anche se le dimensioni cambiano man mano che aggiungi altri elementi.

Per risolvere il problema probabilmente si desidera aggiungere una posizione temporanea e aggiungerla dopo il ciclo o creare una copia dei dati iniziali e aggiungerla all’originale.