Jak uniknąć "ConcurrentModificationException" podczas usuwania elementów z 'ArrayList' podczas iteracji? [duplikat]

To pytanie ma już odpowiedź tutaj:

Próbuję usunąć niektóre elementy z ArrayList podczas iteracji w ten sposób:

for (String str : myArrayList) {
    if (someCondition) {
        myArrayList.remove(str);
    }
}

Oczywiście, dostaję ConcurrentModificationException przy próbie usunięcia elementów z listy w tym samym czasie, gdy iteracja myArrayList. Czy istnieje jakieś proste rozwiązanie tego problemu?

Author: ʇolɐǝz ǝɥʇ qoq, 2013-08-26

10 answers

Użyj Iterator i zadzwoń remove():

Iterator<String> iter = myArrayList.iterator();

while (iter.hasNext()) {
    String str = iter.next();

    if (someCondition)
        iter.remove();
}
 466
Author: arshajii,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-08-26 16:29:00

Jako alternatywę dla odpowiedzi innych zawsze robiłem coś takiego:

List<String> toRemove = new ArrayList<String>();
for (String str : myArrayList) {
    if (someCondition) {
        toRemove.add(str);
    }
}
myArrayList.removeAll(toRemove);

Pozwoli to uniknąć bezpośredniego kontaktu z iteratorem, ale wymaga innej listy. Zawsze wolałem tę trasę z jakiegokolwiek powodu.

 146
Author: Kevin DiTraglia,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-05-09 04:19:12

Java 8 użytkownik może to zrobić: list.removeIf(...)

    List<String> list = new ArrayList<>(Arrays.asList("a", "b", "c"));
    list.removeIf(e -> (someCondition));

Usunie elementy z listy, dla których niektóre elementy są spełnione

 65
Author: Mikhail Boyarsky,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-09-26 12:11:58

Musisz użyć metody remove () iteratora, co oznacza brak enhanced for loop:

for (final Iterator iterator = myArrayList.iterator(); iterator.hasNext(); ) {
    iterator.next();
    if (someCondition) {
        iterator.remove();
    }
}
 55
Author: Eric Stein,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-02-16 20:54:48

Nie, Nie, Nie!

W zadaniach z pojedynczym wątkiem nie trzeba używać iteratora, ponadto CopyOnWriteArrayList (ze względu na hit wydajności).

Rozwiązanie jest znacznie prostsze: spróbuj użyć canonical for loop zamiast for-each loop .

Według właścicieli praw autorskich Javy (kilka lat temu Sun, obecnie Oracle) for-each loop guide , używa iteratora do przechodzenia przez kolekcję i po prostu ukrywa ją, aby Kod wyglądał lepiej. Ale, niestety, jak widać, wyprodukował więcej problemy niż zyski, w przeciwnym razie ten temat nie powstałby.

Na przykład ten kod doprowadzi do Javy.util.ConcurrentModificationException podczas wprowadzania kolejnej iteracji na zmodyfikowanej ArrayList:

        // process collection
        for (SomeClass currElement: testList) {

            SomeClass founDuplicate = findDuplicates(currElement);
            if (founDuplicate != null) {
                uniqueTestList.add(founDuplicate);
                testList.remove(testList.indexOf(currElement));
            }
        }

Ale następujący kod działa dobrze:

    // process collection
    for (int i = 0; i < testList.size(); i++) {
        SomeClass currElement = testList.get(i);

        SomeClass founDuplicate = findDuplicates(currElement);
        if (founDuplicate != null) {
            uniqueTestList.add(founDuplicate);
            testList.remove(testList.indexOf(currElement));
            i--; //to avoid skipping of shifted element
        }
    }

Więc spróbuj użyć metody indeksowania do iteracji nad zbiorami i unikaj pętli for-each, ponieważ nie są one równoważne! For-każda pętla korzysta z iteratorów wewnętrznych, które sprawdzają modyfikację kolekcji i rzucają Wyjątek ConcurrentModificationException. Aby to potwierdzić, przyjrzyj się bliżej wydrukowanemu śladowi stosu w pierwszym przykładzie, który opublikowałem:

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 TestFail.main(TestFail.java:43)

Dla wielowątkowości użyj odpowiednich podejść wielozadaniowych (takich jak zsynchronizowane słowo kluczowe).

 26
Author: Dima Naychuk,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-11-24 01:05:18

Podczas gdy inne sugerowane rozwiązania działają, jeśli naprawdę chcesz, aby rozwiązanie było bezpieczne dla wątku, powinieneś zastąpić ArrayList CopyOnWriteArrayList

    //List<String> s = new ArrayList<>(); //Will throw exception
    List<String> s = new CopyOnWriteArrayList<>();
    s.add("B");
    Iterator<String> it = s.iterator();
    s.add("A");

    //Below removes only "B" from List
    while (it.hasNext()) {
        s.remove(it.next());
    }
    System.out.println(s);
 8
Author: Prashant Bhate,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-08-26 16:55:51

Jedną z alternatywnych metod jest konwersja List na array, iteracja ich i usuwanie ich bezpośrednio z List w oparciu o Twoją logikę.

List<String> myList = new ArrayList<String>(); // You can use either list or set

myList.add("abc");
myList.add("abcd");
myList.add("abcde");
myList.add("abcdef");
myList.add("abcdefg");

Object[] obj = myList.toArray();

for(Object o:obj)  {
    if(condition)
        myList.remove(o.toString());
}
 7
Author: CarlJohn,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-11-21 12:50:20

Jeśli chcesz zmodyfikować swoją listę podczas przechodzenia, musisz użyć Iterator. Następnie możesz użyć iterator.remove(), Aby usunąć elementy podczas trawersowania.

 6
Author: Juned Ahsan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-08-26 16:28:25
List myArrayList  = Collections.synchronizedList(new ArrayList());

//add your elements  
 myArrayList.add();
 myArrayList.add();
 myArrayList.add();

synchronized(myArrayList) {
    Iterator i = myArrayList.iterator(); 
     while (i.hasNext()){
         Object  object = i.next();
     }
 }
 6
Author: Prabhakaran,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-08-26 16:48:55

Możesz użyć funkcji iterator remove (), aby usunąć obiekt z bazowego obiektu collection. Ale w takim przypadku możesz usunąć ten sam obiekt, a nie jakikolwiek inny obiekt z listy.

From here

 1
Author: gilo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-04-22 16:30:43