2011-05-20 19 views
11

Co dziwne, ten mały fragment kodu wyrzuca wyżej wspomniany wyjątek. Ponadto, patrząc na kod pisał po internecie to wydaje się być poprawne:Dlaczego Iterator.next() wyrzuca ConcurrentModificationException

import java.util.ArrayList; 
import java.util.Iterator; 

public class IteratorTest { 

    ArrayList<Integer> arr = new ArrayList<Integer>(); 

    Iterator i = arr.iterator(); 

    public void show() { 
     arr.add(2); 
     arr.add(5); 
     arr.add(9); 

     while(i.hasNext()){ 
      System.out.println(i.next()); 
     } 
    } 
} 

Wszelkie porady? Dzięki

+0

Duplikat http://stackoverflow.com/questions/223918/iterating-through-a-collection-avoiding-concurrentmodificationexception-when-re – Raedwald

Odpowiedz

12

niniejszego zaproszenia:

Iterator i=arr.iterator(); 

powinny być po wykonaniu wszystkich pisze w swojej ArrayList.

Więc w kodzie to zrobić tuż przed rozpoczęciem iteracji tak:

Iterator i=arr.iterator(); 
while(i.hasNext()) { 
... 
} 
+0

Thx anbhava, więc w zasadzie Iterator nie będzie aktualizowana, jeśli do zmiany Iterable są wykonywane. nie wiedziałem, że to jest – JBoy

+1

Myślę, że pętla for jest lepsza niż pętla while, ponieważ ograniczasz zakres zmiennej iteracyjnej "i". –

4

To dlatego, że zmodyfikowano listę podkładową pomiędzy coraz iterator poprzez iterator() i nazywając next().

Typowe wykorzystanie iterator jest:

for (Iterator<Integer> iter=arr.iterator(); iter.hasNext();) { 
    Integer element = iter.next(); 
} 

Albo jeszcze lepiej, korzystać z nowego for-each pętli:

for (Integer element: arr) { 
} 

upewnij się wykonać uzupełnień Kolekcji zewnątrz z pętla.

2

Definiujesz Iterator po utworzeniu obiektu, IteratorTest. Następnie dodajesz niektóre dane do ArrayList, arr.

Zmodyfikowałeś już listę, a zatem stan Iterator jest nieprawidłowy.

Nie można użyć Iterator i zmienić ArrayList, aby wykonać tę czynność bez użycia Iterator.

0

Odpowiedź 4 jest poprawna technicznie, ale nie dlatego, że zamiast pętli while() używana jest pętla for(;;) lub "dla każdego". Jest to po prostu kwestia zadeklarowanego zakresu Iteratora w ramach klasy, a nie metody. Najpierw przyjrzyjmy się zachowaniu dwóch metod: po prostu zapytujemy wewnętrzny kursor (indeks); next() faktycznie przesuwa kursor, dlatego jest to "modyfikacja", która może podnieść wyjątek. Jeśli spróbujesz użyć zadeklarowanej iteracji i przypisanej poza metodą, w której jest używany next(), kod wygeneruje wyjątek na pierwszym next(), niezależnie od tego, czy jest to for(;;) czy while().

W odpowiedziach 4 i 8 iterator jest zadeklarowany i spożytkowany lokalnie w ramach metody. Tak się składa, że ​​konstrukt for(;;) pozwala na deklarację po raz pierwszy i przypisanie iteratora przed wykonaniem pętli (co powoduje, że iterator ma zasięg for(;;) i niejawnie w metodzie, czyniąc go "bezpiecznym"). Zgadzam się, że idiom for(;;) jest czystszy pod względem składniowym i płotowym na najniższym poziomie wykonania, całkowicie w ramach pętli for(;;).

Odpowiedź 8 jest poprawna, ponieważ Iterator jest przypisany i używany lokalnie w metodzie.

Ale tylko dla dobra dyskusji, poniższa metoda z użyciem while() oświadczenie jest składniowo poprawne, „bezpieczny” i bez modyfikacji z punktu widzenia zakresu i odniesienia:

gdzieś w definicji klasy .. .

ArrayList<String> messageList; 

gdzieś w konstruktorze klasy

messageList = new ArrayList<String>(); 

dodać metodę ...

public printMessages () 
{ 

    Iterator<String> messageIterator = messageList.iterator(); 

    while (messageIterator.hasNext()) 
    { 
    System.out.println(messageIterator.next()); 
    } 

    System.out.flush(); 

} 
Powiązane problemy