2010-07-06 41 views
63

Mam następujący fragment kodu:ConcurrentModificationException dla ArrayList

private String toString(List<DrugStrength> aDrugStrengthList) { 
    StringBuilder str = new StringBuilder(); 
     for (DrugStrength aDrugStrength : aDrugStrengthList) { 
      if (!aDrugStrength.isValidDrugDescription()) { 
       aDrugStrengthList.remove(aDrugStrength); 
      } 
     } 
     str.append(aDrugStrengthList); 
     if (str.indexOf("]") != -1) { 
      str.insert(str.lastIndexOf("]"), "\n   "); 
     } 
    return str.toString(); 
} 

Kiedy próbuję go uruchomić, dostaję ConcurrentModificationException, może ktoś wyjaśnić, dlaczego tak się dzieje, nawet jeśli kod jest uruchomiony w tym samym wątku? I jak mogłem tego uniknąć?

+3

[Powinieneś przestać się martwić i pokochałem powtórzeń.] (Http://blog.stackoverflow.com/2010/11/dr-strangedupe-or-how- Nauczyłem się przestać - martwić się i kochać - duplikować /). – Will

+1

Wyjaśnienie tego wyjątku jest takie, że iterator tablicy ArrayList jest iteratorem fail-fast; tj. nie powiedzie się (wyrzuci wyjątku), gdy wykryje, że jego gromadzenie w międzyczasie zostało zmodyfikowane. W porównaniu z iteratorami fail-safe, które nie powodują jednoczesnych modyfikacji wyjątków (np. W kolekcjach ConcurrentHashMap i CopyOnWriteArrayList). –

Odpowiedz

136

Nie można usunąć z listy, jeśli przeglądasz ją z "dla każdego" pętli. Możesz użyć Iterator. Wymienić:

for (DrugStrength aDrugStrength : aDrugStrengthList) { 
    if (!aDrugStrength.isValidDrugDescription()) { 
     aDrugStrengthList.remove(aDrugStrength); 
    } 
} 

Z:

for (Iterator<DrugStrength> it = aDrugStrengthList.iterator(); it.hasNext();) { 
    DrugStrength aDrugStrength = it.next(); 
    if (!aDrugStrength.isValidDrugDescription()) { 
     it.remove(); 
    } 
} 
+0

Składnia foreach java faktycznie używa Iteratora, niektóre IDE zgłoszą to rozwiązanie i zaproponują zastąpienie foreach (dla (słuchacz MyListener: MyListenerList)) –

+0

@ HugoGresse Tak, ale jest to odwrotny kierunek. Iterator ujawnia "usuń", który jest bezpieczny dla jego iteracji, coś, co przepowiada "przegrywa". –

+2

nie wiedziałem, że dziękuję @KonradGarus –

5

Podczas iteracji przez pętlę, próbujesz zmienić wartość List w operacji remove(). Spowoduje to wyjątek ConcurrentModificationException.

Wykonaj poniższy kod, który będzie osiągnąć to, co chcesz i jeszcze nie rzucać żadnych wyjątków

private String toString(List aDrugStrengthList) { 
     StringBuilder str = new StringBuilder(); 
    List removalList = new ArrayList(); 
    for (DrugStrength aDrugStrength : aDrugStrengthList) { 
     if (!aDrugStrength.isValidDrugDescription()) { 
      removalList.add(aDrugStrength); 
     } 
    } 
    aDrugStrengthList.removeAll(removalList); 
    str.append(aDrugStrengthList); 
    if (str.indexOf("]") != -1) { 
     str.insert(str.lastIndexOf("]"), "\n   "); 
    } 
    return str.toString(); 
} 
+0

Dlaczego w dół? – bragboy

+2

'aDrugStrengthList.removeAll (removalList)' –

+0

@TimBender - dziękuję zredagowałeś odpowiedź. – bragboy

22

Podobnie jak inne odpowiedzi powiedzenia, nie można usunąć element z kolekcji jesteś iteracji nad . Możesz obejść to poprzez jawne użycie Iterator i usunięcie tego elementu.

5

nie powinien posiada współbieżne implementacja interfejsu listy wspierającego taką operację.

spróbować java.util.concurrent.CopyOnWriteArrayList.class

+0

Miałem ten sam problem z HashMap, naprawiony z inną implementacją interfejsu Map. Powinieneś przetestować to sam. Nie wiem szczegółowo o CopyOnWriteArrayList – idiotgenius

12

Lubię odwrotnej kolejności do pętli takich jak:

int size = list.size(); 
for (int i = size - 1; i >= 0; i--) { 
    if(remove){ 
     list.remove(i); 
    } 
} 

ponieważ nie wymaga uczenia się nowych struktur danych lub klas.

+0

WOW DZIĘKUJEMY. Nie znałem tej sztuczki. –

Powiązane problemy