2013-02-21 16 views
6

kiedy wyjąć drugi ostatni element nie ma 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); 

ale kiedy usunąć inne elementy jest 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); 

co jest tego powodem?

+3

Proszę uważnie przeczytać pytanie. – user1947415

+0

W pierwszym kawałku kodu występuje również błąd "Wyjątek w wątku" główny "java.util.ConcurrentModificationException". Nie można aktualizować tej samej kolekcji podczas jej iterowania. –

+0

Dlaczego nie dostałem wyjątku? Próbowałeś? – user1947415

Odpowiedz

3

widzę to samo,

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

który wyprowadza,

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) 

I znalazłem the reason.

+1

Tak. To właśnie mam na myśli. – user1947415

+0

@ user1947415, dodałem link, który może okazać się przydatny. – mre

3

Java używa modCount (liczba modyfikacji) i expectedCount, aby sprawdzić, czy istnieje modyfikacja do listy.

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

zarówno w stanie, modCount jest 6 po usuwania, ale expectedModCount to 5.

Problemem jest hasNext().

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

Lista korzysta z kursora i rozmiaru, aby sprawdzić, czy ma następny element. A funkcja hasNext() ma miejsce przed checkForComodification, ponieważ checkForComodification() jest wywoływana w metodzie 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]; 
    } 

Tak więc po usunięciu drugiego ostatniego elementu, kursor = 4 i rozmiar = 4 również. hasNext() zwraca false. Wyskocz z pętli i wydrukuj wynik.

+0

Ale, moim zdaniem, jedyną różnicą między dwoma fragmentami kodu jest to, że pierwszy usuwa "str4", a drugi usuwa "str1". Ale pierwsza z nich działa pomyślnie. Drugi rzuca wyjątek ConcurrentModificationException. – user1947415

+1

Zaktualizowano odpowiedź. – StarPinkER

+0

Właśnie z tego powodu nie udało się usunąć drugiego elementu. +1 Jermaine Xu –

-2

Jest to często występujący problem. StackOverflow zawiera setki wątków na ten temat. Można znaleźć odpowiedź na swoje pytanie tutaj:

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

Po usunięciu drugiego ostatni element The hasNext() nie powiedzie się sprawdzenie i przystanki pętli iteracji. Sprawdź kod iteratora ArrayList w JDK.

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

Jednak w przypadku usuwania drugi element hasNext() przechodzi sprawdzić i następną metodę() gdzie pierwsza rzecz to sprawdza jest modyfikacja ArrayList i stąd wyjątkiem wejść. Proszę sprawdzić ten kod:

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

Najbezpieczniejszym sposobem jest usunięcie elementu przy użyciu iteratory usunąć metodę.

Wypróbuj debugger, aby przejść do kodu, aby lepiej zrozumieć, jak to działa.

+1

Nie dostałeś mnie chłopaki. Rozumiem, że muszę użyć Iteratora, aby usunąć element podczas iteracji przez niego. Ale moje pytanie brzmi, dlaczego pierwszy fragment kodu nie rzucił wyjątku ConcurrentModificationException zgodnie z oczekiwaniami. Mam test, który zdarza się tylko wtedy, gdy usuniesz drugi ostatni element. – user1947415

+0

Zaktualizowałem swoją odpowiedź, Aby zrozumieć tego typu kwestie Debugger jest najlepszym rozwiązaniem. –

2

Rzeczywisty kod, który buduje dla for-each javac jest

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

i to jest realizacja ArrayList Iterator.hasNext

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

jak widzimy hasNext() nie sprawdza dla równoczesnej modyfikacji więc gdy usuwamy ostatni, ale jeden element kończy się bez zauważenia problemu.

Właściwie to dziwne, że next() i remove() sprawdzić dla równoczesnej modyfikacji, ale hasNext() nie. Fail-szybki iterator powinien wykrywać błędy, ale nasz błąd nie został zauważony.

Powiązane problemy