2013-02-21 7 views
6

wenn entfernt das zweite letzte Element keine ConcurrentModificationException istJava 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); 

Aber wenn entfernen andere Elemente eine ConcurrentModificationException ist

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); 

was ist der Grund?

+3

Bitte lesen Sie die Frage sorgfältig Jungs. – user1947415

+0

In Ihrem ersten Stück Code gibt es auch Fehler "Ausnahme im Thread" main "java.util.ConcurrentModificationException". Sie können die gleiche Sammlung nicht aktualisieren, wenn Sie darüber iterieren. –

+0

Warum habe ich die Ausnahme nicht bekommen? Hast du es versucht? – user1947415

Antwort

3

ich die gleiche Sache zu sehen bin,

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(); 
     } 
    } 
} 

welche Ausgänge

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) 

Und ich habe the reason gefunden.

+1

Ja. Das ist was ich meine Jungs. – user1947415

+0

@ user1947415, ich habe einen Link hinzugefügt, der sich als nützlich erweisen kann. – mre

3

Java verwendet einen modCount (Änderungszähler) und einen expectedCount, um zu testen, ob eine Änderung an der Liste vorgenommen wurde.

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

In beiden Zustand ist modCount 6 nach dem entfernen, aber expectedModCount ist 5.

Das Problem der hasNext ist().

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

Die Liste verwendet einen Cursor und eine Größe, um zu überprüfen, ob ein nächstes Element vorhanden ist. Und die hasNext() ist vor der checkForCodification passiert, weil die checkForComodification() in der next() -Methode aufgerufen wird.

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]; 
    } 

Also, wenn Sie das zweitletzte Element entfernen, ist der Cursor = 4 und Größe = 4 auch. hasNext() gibt false zurück. Springen Sie aus der Schleife und drucken Sie das Ergebnis.

+0

Aber mein Punkt ist, der einzige Unterschied zwischen zwei Teilen des Codes ist, dass der erste "str4" entfernt, während der zweite "str1" entfernt. Aber die erste läuft erfolgreich. Der zweite wirft eine ConcurrentModificationException. – user1947415

+1

Aktualisierte die Antwort. – StarPinkER

+0

Dies ist genau der Grund, warum es fehlgeschlagen ist, wenn Sie das zweite Element entfernt haben. +1 Jermaine Xu –

-2

Dies ist ein häufig auftretendes Problem. StackOverflow hat Hunderte von Threads, die dies abdecken. Sie können die Antwort auf Ihre Frage finden Sie hier:

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

Wenn Sie das zweite letzte Element entfernen, die hasNext() Prüfung fehlschlägt und die Schleifeniterationslatenzzeit stoppt. Überprüfen Sie den ArrayList-Iteratorcode in JDK.

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

Aber im Fall der Entfernung des zweiten Elements der hasNext() Kontrolle Pässen und geben Sie die Methode next(), wo das erste, was es für eine Änderung an die Arraylist ist überprüft und damit die Ausnahme. Bitte überprüfen Sie diesen Code:

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

Der sicherste Weg ist es, das Element zu entfernen, indem die Iteratoren Methode entfernen.

Versuchen Debugger über den Code zu dem Schritt, für ein besseres Verständnis davon, wie es funktioniert.

+1

Sie haben mich nicht Jungs bekommen. Ich verstehe, dass ich Iterator verwenden muss, um Element zu entfernen, während es durchlaufen wird. Aber meine Frage ist, warum der erste Codeabschnitt keine ConcurrentModificationException wie erwartet ausgelöst hat. Ich habe Test das passiert nur, wenn Sie das vorletzte Element entfernen. – user1947415

+0

Ich habe meine Antwort aktualisiert, Um diese Art von Problemen zu verstehen Debugger ist Ihre beste Wette –

2

Der eigentliche Code, dass javac für for-each baut ist

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

und dies ist Arraylist Iterator.hasNext Implementierung

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

wie wir hasNext() sehen können nicht für die gleichzeitige Änderung überprüfen, so dass, wenn wir entfernen Das vorletzte Element der Schleife endet, ohne das Problem zu bemerken.

Eigentlich ist es seltsam, dass next() und remove() für gleichzeitige Änderung überprüfen, aber hasNext() nicht tut. Der Fail-Fast-Iterator soll Bugs erkennen, aber unser Bug ist unbemerkt geblieben.

Verwandte Themen