2013-02-26 16 views
11

Mam dwie klasy niżej javaLista wyrzuca ConcurrentModificationException, ale zestaw nie generuje ConcurrentModificationException?

import java.util.*; 

public class ArrayListTest032 { 
    public static void main(String[] ar) { 
     List<String> list = new ArrayList<String>(); 
     list.add("core java"); 
     list.add("php"); 
     list.add("j2ee"); 
     list.add("struts"); 
     list.add("hibernate"); 

     Iterator<String> itr = list.iterator(); 

     while (itr.hasNext()) { 
      System.out.println(itr.next()); 
     } 
     list.remove("php"); 

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

    } 
} 

Kiedy uruchomić powyższy kod otrzymuję poniżej wyjścia.

core java 
php 
j2ee 
struts 
hibernate 

Exception in thread "main" java.util.ConcurrentModificationException 
    at java.util.AbstractList$Itr.checkForComodification(AbstractList.java:372) 
    at java.util.AbstractList$Itr.next(AbstractList.java:343) 
    at ArrayListTest032.main(ArrayListTest032.java:20) 

Co jest oczekiwane, ponieważ modyfikuję listę podczas iteracji. Ale w poniższej klasie java ta sama logika jest wykonywana przez ustawioną rodzinę.

import java.util.*; 

public class HashSetTest021 { 
    public static void main(String[] ar) { 
     Set<String> set = new HashSet<String>(); 
     set.add("core java"); 
     set.add("php"); 
     set.add("j2ee"); 
     set.add("struts"); 
     set.add("hibernate"); 

     Iterator<String> itr = set.iterator(); 

     while (itr.hasNext()) { 
      System.out.println(itr.next()); 
     } 
     set.remove("php"); 

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

    } 
} 

I na zewnątrz jest.

hibernate 
core java 
j2ee 
php 
struts 

Nie ma żadnego ConcurrentModificationException.

Chcę tylko wiedzieć, dlaczego sam kawałek kodu rzuca ConcurrentModificationException w przypadku list rodziny, ale nie ma żadnego ConcurrentModificationException w przypadku set rodziny

Odpowiedz

4

Jest to różnica w implementacji: iterator zwracany przez listę tablic wykrywa równoczesne modyfikacje, nawet gdy jest umieszczony na końcu, ponieważ sprawdza długość; Iteratory z HashSet, TreeSet i i nie wykrywają tego warunku, ponieważ sprawdzają, czy są ustawione na końcu przed sprawdzeniem, czy są współbieżne. Dokumentacja pozwala iteratorom nie nakładać na równoczesne modyfikacje, więc oba podejścia są prawidłowe.

+0

+1 dla demo fiddle –

+0

nit: Iterator ArrayList faktycznie sprawdza liczbę modyfikacji, a nie długość jako taką, więc jeśli dodasz, natychmiast usuniesz element, pozostawiając taką samą długość, po tym otrzymasz ConurrentModificationException. –

1
public static void main(String[] ar) { 
      List<String> list = new ArrayList<String>(); 
      list.add("core java"); 
      list.add("php"); 
      list.add("j2ee"); 
      list.add("struts"); 
      list.add("hibernate"); 

      Iterator<String> itr = list.iterator(); 

      while (itr.hasNext()) { 
       System.out.println(itr.next()); 
      } 
      list.remove("php"); 

      /* while (itr.hasNext()) { 
       System.out.println(itr.next()); 
      }*/ 

     } 

problem in itr object.it holds the list object reference 
+0

to dlaczego zestaw nie zgłasza wyjątku. możesz sprawdzić obie klasy: –

+0

Ze śledzenia stosu wyjściowego wynika, że ​​wyjątek nadchodzi, gdy wywołujemy funkcję iterator next(). Jeśli zastanawiasz się, w jaki sposób Iterator sprawdza modyfikację, jego implementacja jest obecna w klasie AbstractList, w której zdefiniowano zmienną typu int modCount, która zapewnia liczbę zmian wielkości listy. – Biswajit

5

Jest to rodzaj " wstecznego zachowania, tak jak iteratory, po pełnym przejściu, nie można ich użyć ponownie, inaczej metoda hasNext powinna zwrócić wartość false, gdy dojdziesz do końca listy.

W tym przypadku jednak, iterator zwrócony przez ArrayList.iterator to wewnętrzna klasa wdrożenia, z kodem do hasNext następująco:

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

Więc kiedy zadzwonisz hasNext w drugiej pętli, oznacza to (niesłusznie), że istnieje więcej elementów do iteracji, ponieważ wykonano operację, która zmieniła rozmiar listy po pierwszej iteracji. Semantycznie, nie możesz kontynuować powtarzania pozycji na liście po jej osiągnięciu, ale dzięki temu szczegółowi implementacji możesz kontynuować drugą pętlę. Oczywiście w tym momencie otrzymasz wyjątek modyfikacji współbieżnej z powodu zmiany dokonanej na liście kopii.

Z drugiej strony, iterator używany przez zestaw hash ma swój hasNext realizowane w następujący sposób:

public final boolean hasNext() { 
    return next != null; 
} 

Ta implementacja nie dzieje się za „wrażliwe” do modyfikacji wprowadzonych do mieszania ustawiony po iteracji została zakończona i jako taka lepiej zachowuje się metoda hasNext.

+0

OK, a w przypadku ustawionej rodziny? –

+0

@Real - dodano szczegóły implementacji zestawu hash. – Perception

2

Zacznij od przeczytaniadla Iterator. Czy w ogóle wspomina o ConcurrentModificationException?

Teraz odczytu Javadoc do ConcurrentModificationException, a należy pamiętać, że (podkreślenie)

ten wyjątek może być wyrzucane za pomocą metod, które są wykrywane równoległe modyfikacji obiektu gdy takie modyfikacje nie jest dopuszczalne.

Teraz spójrz na swój kod. Twoja pętla while iteruje przez wszystkie elementy kolekcji (nawet jeśli wyjście z pierwszego przykładu tego nie wskazuje, co oznacza, że ​​albo edytowałeś dane wyjściowe, albo to nie jest twój rzeczywisty kod). W momencie, gdy usuniesz element, nie ma już elementów do iteracji, więc druga pętla powinna zawsze wyjść natychmiast.

Zatem konkluzja jest taka, że ​​realizatorzy listy iterator mieć wybrany że aby rzucić ten wyjątek, nawet gdy nie ma więcej elementów do iteracji, natomiast realizatorzy zestawu iterator mieć wybrany nie robić. Oba przypadki są całkowicie dopuszczalne, biorąc pod uwagę specyfikacje.

+0

Dodałem tę samą klasę w powyższym kodzie, możesz uruchomić kod i sprawdzić dane wyjściowe samodzielnie. –

+0

@Real - tak, uruchomiłem go, a wynik wyświetlił "hibernacja", której nie publikujesz. Ale tak naprawdę, zamiast rzucać krótkie spodenki na temat nawiasowego komentarza, powinieneś pomyśleć o * treści * mojej odpowiedzi. – parsifal

+0

Każdy pomysł, dlaczego ten projekt, w którym lista wyrzuca i zestaw nie jest preferowany? – djechlin

1

Hashset może rzucić wyjątek ConcurrentModificationException, jeśli zrobisz cokolwiek do zestawu, za wyjątkiem iteratora. Istnieje jednak wiele heurystyk związanych z szybkim błędem itertora, których celem jest zakończenie iteracji, o ile to możliwe. JavaDocs wydaje się dość jasny na jego zachowanie.

0

W przypadku, gdy liście przechodzenie jej pierwszej pętli Iterator itr = set.iterator();

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

Wartość kursor i rozmiar będzie same.Cursor zawiera wartość całkowita liczba elementów i przepływa wewnątrz hashNext() Sposób przechodzenia lista zawiera kod jako:

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

Tak więc po raz pierwszy, a pętla kursor == rozmiar.Ale po usunięciu elementu z listy rozmiar staje się (originalSize-1). Tak, aby przejść do następnej pętli, wchodzi do środka, natomiast wewnątrz metody itr.next() sprawdza modyfikację modcount i generuje wyjątek ConcurrentModificationException.

W przypadku ustawić go sprawdza następny! = Null dla każdego itr.hasnext() call.And po przejściu pierwszej pętli while obok staje null.Removing element z zestawu nie wpływa na następną wartość jako nieważną i itr.hasNext będzie return next == null jako true, a więc nie wchodzi w pętlę podczas sprawdzania modyfikacji modcount. A zatem nie powoduje wyjątku ConcurrentModification.