Ottenere una ConcurrentModificationException generata durante la rimozione di un elemento da un java.util.List durante l’iterazione di lista?

@Test public void testListCur(){ List li=new ArrayList(); for(int i=0;i<10;i++){ li.add("str"+i); } for(String st:li){ if(st.equalsIgnoreCase("str3")) li.remove("str3"); } System.out.println(li); } 

Quando eseguo questo codice, lancerò una ConcurrentModificationException.

Sembra che quando rimuovo l’elemento specificato dalla lista, la lista non sa che le sue dimensioni sono state cambiate.

Mi chiedo se questo è un problema comune con le raccolte e la rimozione di elementi?

Credo che questo sia lo scopo alla base del metodo Iterator.remove () , per essere in grado di rimuovere un elemento dalla collezione mentre si sta iterando.

Per esempio:

 Iterator iter = li.iterator(); while(iter.hasNext()){ if(iter.next().equalsIgnoreCase("str3")) iter.remove(); } 

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 sta iterando sulla raccolta con un iteratore fail-fast, l’iteratore mostrerà questa eccezione

Tratto da http://download.oracle.com/javase/1.4.2/docs/api/java/util/ConcurrentModificationException.html

Il modo Java 8 per rimuoverlo dall’elenco senza Iterator è:

 li.removeIf() 

vale a dire

 List li = new ArrayList(); // ... li = li.removeIf(st -> !st.equalsIgnoreCase("str3")); 

sì, ci sono persone che vi si imbattono – il problema è che non è ansible modificare l’elenco mentre si itera su di esso. Ho usato 2 alternative in passato:

  1. Puoi tenere traccia degli indici degli elementi che desideri rimuovere e quindi rimuoverli dopo aver terminato l’iterazione.
  2. Oppure puoi copiare tutti quelli che vuoi tenere in una nuova lista mentre esegui l’iterazione, e poi scartare la vecchia lista quando hai finito.

tali opzioni presuppongono che sia necessario scorrere l’elenco per trovare gli elementi da rimuovere, utile nei casi in cui gli elementi dell’elenco sono oggetti complessi con proprietà su cui è ansible eseguire il test.

Nel tuo caso particolare, non hai nemmeno bisogno di iterare, dato che puoi semplicemente usare removeAll. Guarda l’API qui . Esistono anche ottimi metodi come retainAll che scarta tutto ciò che non è nell’argomento. È ansible utilizzare i metodi remove / retain-like ogni volta che gli oggetti nella lista implementano equamente e hashcode correttamente. Se non puoi fare affidamento su equals / hashcode per identificare l’uguaglianza tra le istanze nella tua app, dovrai farlo tu stesso ….

Penso che valga la pena citare la versione di Java 8

 @Test public void testListCur() { List li = new ArrayList(); for (int i = 0; i < 10; i++) { li.add("str" + i); } li = li.stream().filter(st -> !st.equalsIgnoreCase("str3")).collect(Collectors.toList()); System.out.println(li); } 

Ho avuto questo problema e penso che il modo più semplice sia lo stesso con il secondo modo che ha dato hvgotcodes.

Oppure puoi copiare tutti quelli che vuoi tenere in una nuova lista mentre esegui l’iterazione, e poi scartare la vecchia lista quando hai finito.

 @Test public void testListCur(){ List li=new ArrayList(); for(int i=0;i<10;i++){ li.add("str"+i); } List finalLi = new ArrayList(); for(String st:li){ if(st.equalsIgnoreCase("str3")){ // Do nothing } else { finalLi.add(st); } } System.out.println(finalLi); } 

ArrayList ha campo modCount – conteggio delle modifiche alla collezione

Quando invochi il metodo iterator() crea un nuovo object Itr . È expectedModCount . expectedModCount field initialize by modCount value. Quando invochi

 li.remove("str3"); 

incrementi di modCount . Quando provi ad accedere a li via iteratore, controlla quanto expectedModCount == modCount

e se è falso lancia ConcurrentModificationException

Quindi se si ottiene iteratore e dopo la raccolta modificata – l’iteratore è considerato non valido e non è ansible utilizzarlo.

Prova questo (Java 8):

 list.removeIf(condition); 

Ho fatto un giro in un modo diverso …

 public void testListCur(){ List li=new ArrayList(); for(int i=0;i<10;i++){ li.add("str"+i); } for(int i=0; i
  • Si può fare una copia della lista da cui si desidera rimuovere l’elemento, direttamente in per ogni ciclo. Per me, questo è il modo più semplice. Qualcosa come questo:

     for (String stringIter : new ArrayList(myList)) { myList.remove(itemToRemove); } 

    Spero che ti possa aiutare ..

    Penso che la migliore risposta sia da parte di bigdev.de, ma vorrei aggiungere qualcosa (come se l’elemento fosse rimosso da una lista, forse vorresti registrarlo da qualche parte o qualcosa del genere):

     List list = new ArrayList<>(); list.removeIf(a -> { boolean condition = a.equalsIgnoreCase("some condition"); if(condition) logger.info("Item removed from the list: " + a); return condition; });