2013-02-21 12 views
6

quando rimuovere il secondo ultimo elemento non c'è ConcurrentModificationExceptionJava ConcurrentModificationException

List<String> myList1 = new ArrayList<String>(); 
Collections.addAll(myList1, "str1","str2","str3","str4","str5"); 
for(String element : myList1){//no ConcurrentModificationException here 
if(element.equalsIgnoreCase("str4")) 
    myList1.remove("str4"); 
} 
System.out.println(myList1); 

Ma quando rimuova altri elementi v'è un ConcurrentModificationException

List<String> myList2 = new ArrayList<String>(); 
Collections.addAll(myList2, "str1","str2","str3","str4","str5"); 
for(String element : myList2){//ConcurrentModificationException here 
if(element.equalsIgnoreCase("str1")) 
    myList2.remove("str1"); 
} 
System.out.println(myList2); 

qual è la ragione?

+3

Si prega di leggere attentamente la domanda ragazzi. – user1947415

+0

Nel tuo primo pezzo di codice c'è anche l'errore "Exception in thread" main "java.util.ConcurrentModificationException". Non è possibile aggiornare la stessa raccolta quando si sta iterando su di essa. –

+0

Perché non ho ricevuto l'eccezione? Hai provato? – user1947415

risposta

3

sto vedendo la stessa cosa,

import java.util.ArrayList; 
import java.util.Collections; 
import java.util.List; 

public class Launcher 
{ 
    public static void main(String[] args) 
    { 
     doThis(); 
     doThat(); 
    } 

    private static void doThis() 
    { 
     System.out.println("dothis"); 
     try 
     { 
      List<String> myList1 = new ArrayList<String>(); 
      Collections.addAll(myList1, "str1","str2","str3","str4","str5"); 
      for(String element : myList1){//no ConcurrentModificationException here 
      if(element.equalsIgnoreCase("str4")) 
       myList1.remove("str4"); 
      } 
      System.out.println(myList1); 
     } 
     catch(Exception e) 
     { 
      e.printStackTrace(); 
     } 
    } 

    private static void doThat() 
    { 
     System.out.println("dothat"); 
     try 
     { 
      List<String> myList2 = new ArrayList<String>(); 
      Collections.addAll(myList2, "str1","str2","str3","str4","str5"); 
      for(String element : myList2){//ConcurrentModificationException here 
      if(element.equalsIgnoreCase("str1")) 
       myList2.remove("str1"); 
      } 
      System.out.println(myList2); 
     } 
     catch(Exception e) 
     { 
      e.printStackTrace(); 
     } 
    } 
} 

che emette,

dothis 
[str1, str2, str3, str5] 
dothat 
java.util.ConcurrentModificationException 
    at java.util.AbstractList$Itr.checkForComodification(Unknown Source) 
    at java.util.AbstractList$Itr.next(Unknown Source) 
    at com.foo.Launcher.doThat(Launcher.java:41) 
    at com.foo.Launcher.main(Launcher.java:12) 

E ho trovato the reason.

+1

Sì. Questo è quello che intendo ragazzi. – user1947415

+0

@ user1947415, ho aggiunto un collegamento che potrebbe rivelarsi utile. – mre

3

Java utilizza un modCount (conteggio delle modifiche) e un conteggio previsto per verificare se è presente una modifica all'elenco.

final void checkForComodification() { 
    if (modCount != expectedModCount) 
     throw new ConcurrentModificationException(); 
} 

In entrambi condizione, modCount è 6 dopo la rimozione, ma è expectedModCount 5.

Il problema è il hasNext().

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

lista utilizzano un cursore e dimensione per verificare se ha un elemento successivo. E hasNext() è preceduto da checkForComodification perché checkForComodification() viene chiamato nel metodo next().

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

    @SuppressWarnings("unchecked") 
    public E next() { 
     checkForComodification(); 
     int i = cursor; 
     if (i >= size) 
      throw new NoSuchElementException(); 
     Object[] elementData = ArrayList.this.elementData; 
     if (i >= elementData.length) 
      throw new ConcurrentModificationException(); 
     cursor = i + 1; 
     return (E) elementData[lastRet = i]; 
    } 

Quindi quando si rimuove il penultimo elemento, anche il cursore = 4 e anche la dimensione = 4. hasNext() restituisce false. Salta fuori dal giro e stampa il risultato.

+0

Ma, il mio punto è, l'unica differenza tra due pezzi di codice è che il primo rimuove lo "str4" mentre il secondo rimuove "str1". Ma il primo viene eseguito con successo. Il secondo lancia una ConcurrentModificationException. – user1947415

+1

Aggiornamento della risposta. – StarPinkER

+0

Questo è esattamente il motivo per cui non è riuscito quando si è rimosso il secondo elemento. +1 Jermaine Xu –

-2

Questo è un problema comune. StackOverflow ha centinaia di thread che coprono questo. È possibile trovare la risposta alla tua domanda qui:

How can I iterate over an object while modifying it in Java?

Quando si rimuove il secondo ultimo elemento, il hasNext() controllo ha esito negativo e le fermate del ciclo di iterazione. Controlla il codice iteratore ArrayList in JDK.

http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/6-b14/java/util/ArrayList.java#ArrayList.Itr.hasNext%28%29

Ma in caso di rimozione del secondo elemento le hasNext() passa di controllo e si entra nel metodo next(), dove la prima cosa che controlla sia la modifica al ArrayList e, quindi, l'eccezione. Si prega di verificare questo codice:

http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/6-b14/java/util/ArrayList.java#ArrayList.Itr.next%28%29

Il modo più sicuro è quello di rimuovere l'elemento utilizzando le iteratori rimuovono metodo.

Prova debugger per scavalcare il codice, per una migliore comprensione di come funziona.

+1

Non mi hai preso ragazzi. Capisco che ho bisogno di usare Iterator per rimuovere l'elemento mentre lo itera attraverso. Ma la mia domanda è perché il primo pezzo di codice non ha lanciato una ConcurrentModificationException come previsto. Ho provato questo accade solo quando si rimuove il penultimo elemento. – user1947415

+0

Ho aggiornato la mia risposta, per comprendere questo genere di problemi, Debugger è la soluzione migliore. –

2

Il codice che javac costruisce per for-each è

Iterator<String> i = myList1.iterator(); 
    while(i.hasNext()) { 
     String element = i.next(); 
     if (element.equalsIgnoreCase("str4")) 
      myList1.remove("str4"); 
    } 

e questo è implementazione ArrayList Iterator.hasNext

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

come possiamo vedere hasNext() non verifica per la modifica simultanea così quando togliamo l'ultimo ma un elemento il ciclo termina senza notare il problema.

In realtà è strano che next() e remove() verifichi la modifica simultanea ma non lo sia hasNext(). L'iteratore Fail-Fast dovrebbe rilevare i bug, ma il nostro bug è passato inosservato.

Problemi correlati