Dlaczego nie dostanę Javy?util.ConcurrentModificationException w tym przykładzie?

Uwaga: znam metodę Iterator#remove().

W poniższym przykładzie kodu nie rozumiem, dlaczego metoda List.remove in main rzuca ConcurrentModificationException, ale nie w metodzie remove.

public class RemoveListElementDemo {    
    private static final List<Integer> integerList;

    static {
        integerList = new ArrayList<Integer>();
        integerList.add(1);
        integerList.add(2);
        integerList.add(3);
    }

    public static void remove(Integer toRemove) {
        for(Integer integer : integerList) {
            if(integer.equals(toRemove)) {                
                integerList.remove(integer);
            }
        }
    }

    public static void main(String... args) {                
        remove(Integer.valueOf(2));

        Integer toRemove = Integer.valueOf(3);
        for(Integer integer : integerList) {
            if(integer.equals(toRemove)) {                
                integerList.remove(integer);
            }
        }
    }
}
Author: Tunaki, 2011-11-18

10 answers

Oto dlaczego: Jak to jest napisane w Javadoc:

Iteratory zwracane przez iteratora i listeratora tej klasy metody są Fail-fast: jeśli lista jest strukturalnie zmodyfikowana w dowolnym czasu po utworzeniu iteratora, w jakikolwiek sposób, z wyjątkiem własne metody usuwania lub dodawania iteratora, iterator wyrzuci ConcurrentModificationException.

To sprawdzenie odbywa się w metodzie next() iteratora (jak widać po stacktrace). Ale będziemy osiągnij metodę next() tylko wtedy, gdy hasNext() dostarczy true, co jest wywoływane przez for each, aby sprawdzić, czy granica jest spełniona. W Twojej metodzie Usuń, gdy hasNext() sprawdzi, czy musi zwrócić inny element, zobaczy, że zwrócił dwa elementy, a teraz po usunięciu jednego elementu lista zawiera tylko dwa elementy. Więc wszystko jest w porządku i skończyliśmy z iteracją. Sprawdzanie współbieżnych modyfikacji nie zachodzi, ponieważ odbywa się to w metodzie next(), która nigdy nie jest dzwoniłem.

Następnie przechodzimy do drugiej pętli. Po usunięciu drugiej liczby metoda hasNext sprawdzi ponownie czy może zwrócić więcej wartości. Zwrócił już dwie wartości, ale lista zawiera teraz tylko jedną. Ale kod tutaj jest:

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

1 != 2, więc kontynuujemy metodę next(), która teraz uświadamia sobie, że ktoś namieszał w liście i uruchamia wyjątek.

Mam nadzieję, że to wyjaśni twoje pytanie.

Podsumowanie

List.remove() nie rzuci ConcurrentModificationException kiedy usuwa drugi ostatni element z listy.

 265
Author: pushy,
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-02-05 03:36:06

Jednym ze sposobów radzenia sobie z nim jest usunięcie czegoś z kopii Collection (nie samej kolekcji), jeśli dotyczy. Clone oryginalny zbiór to zrobić kopię przez Constructor.

Ten wyjątek może być wyrzucany przez metody, które wykryły współbieżne modyfikacja obiektu, gdy taka modyfikacja nie jest dozwolona.

W twoim konkretnym przypadku, po pierwsze, nie sądzę final jest to sposób, aby przejść biorąc pod uwagę, że zamierzasz zmodyfikować listę przeszłości deklaracja

private static final List<Integer> integerList;

Rozważ również modyfikację kopii zamiast oryginalnej listy.

List<Integer> copy = new ArrayList<Integer>(integerList);

for(Integer integer : integerList) {
    if(integer.equals(remove)) {                
        copy.remove(integer);
    }
}
 42
Author: James Raitsev,
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
2011-11-18 21:58:48

Metoda forward / iterator nie działa podczas usuwania elementów. Możesz usunąć element bez błędu, ale podczas próby uzyskania dostępu do usuniętych elementów pojawi się błąd środowiska wykonawczego. Nie możesz użyć iteratora, ponieważ, jak pokazuje pushy, spowoduje to ConcurrentModificationException, więc zamiast tego użyj zwykłej pętli for, ale krok wstecz przez nią.

List<Integer> integerList;
integerList = new ArrayList<Integer>();
integerList.add(1);
integerList.add(2);
integerList.add(3);

int size= integerList.size();

//Item to remove
Integer remove = Integer.valueOf(3);

Rozwiązanie:

Przemierzaj tablicę w odwrotnej kolejności, jeśli chcesz usunąć element listy. Po prostu przez przechodząc wstecz przez Listę unikasz odwiedzania elementu, który został usunięty, co usuwa wyjątek.

//To remove items from the list, start from the end and go backwards through the arrayList
//This way if we remove one from the beginning as we go through, then we will avoid getting a runtime error
//for java.lang.IndexOutOfBoundsException or java.util.ConcurrentModificationException as when we used the iterator
for (int i=size-1; i> -1; i--) {
    if (integerList.get(i).equals(remove) ) {
        integerList.remove(i);
    }
}
 15
Author: RightHandedMonkey,
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-10-03 11:23:41

Ten fragment będzie Zawsze rzucać ConcurrentModificationException.

Reguła brzmi: "nie możesz modyfikować (dodawać lub usuwać elementów z listy) podczas iteracji za pomocą iteratora (co się dzieje, gdy używasz pętli for-each)".

JavaDocs:

Iteratory zwracane przez metody iterator i listIterator tej klasy są Fail-fast: jeśli lista zostanie strukturalnie zmodyfikowana w dowolnym momencie po utworzeniu iteratora, w jakikolwiek sposób z wyjątkiem własne metody usuwania lub dodawania iteratora, iterator wyrzuci ConcurrentModificationException.

Dlatego jeśli chcesz zmodyfikować listę (lub dowolną kolekcję w ogóle), Użyj iteratora, ponieważ wtedy jest on świadomy modyfikacji i dlatego będą one obsługiwane poprawnie.

Mam nadzieję, że to pomoże.

 8
Author: Bhushan,
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
2011-11-18 21:41:49

Miałem ten sam problem, ale na wypadek, gdybym dodawał en element do iterowanej listy. I made it this way

public static void remove(Integer remove) {
    for(int i=0; i<integerList.size(); i++) {
        //here is maybe fine to deal with integerList.get(i)==null
        if(integerList.get(i).equals(remove)) {                
            integerList.remove(i);
        }
    }
}

Teraz wszystko idzie dobrze, ponieważ nie tworzysz żadnego iteratora nad listą, iterujesz nad nią "ręcznie". I warunek i < integerList.size() nigdy cię nie zwiedzie, ponieważ po usunięciu / dodaniu czegoś do rozmiaru listy zmniejszanie/zwiększanie listy..

Mam nadzieję, że to pomoże, dla mnie to było rozwiązanie.
 4
Author: Gondil,
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-08-22 13:54:54

Jeśli używasz kolekcji copy-on-write to będzie działać; jednak gdy używasz list.iterator (), zwracany Iterator będzie zawsze odwoływał się do kolekcji elementów tak, jak to było, gdy ( jak poniżej ) lista.wywołana została metoda iterator (), nawet jeśli inny wątek modyfikuje kolekcję. Dowolne metody mutacji wywoływane na Iteratorze opartym na copy-on-write lub Listeratorze dodaj, ustaw lub usuń) spowoduje wyrzucenie nieobsługiwanej operacji.

import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;

public class RemoveListElementDemo {    
    private static final List<Integer> integerList;

    static {
        integerList = new CopyOnWriteArrayList<>();
        integerList.add(1);
        integerList.add(2);
        integerList.add(3);
    }

    public static void remove(Integer remove) {
        for(Integer integer : integerList) {
            if(integer.equals(remove)) {                
                integerList.remove(integer);
            }
        }
    }

    public static void main(String... args) {                
        remove(Integer.valueOf(2));

        Integer remove = Integer.valueOf(3);
        for(Integer integer : integerList) {
            if(integer.equals(remove)) {                
                integerList.remove(integer);
            }
        }
    }
}
 1
Author: JohnnyO,
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
2015-02-24 20:05:12

To działa dobrze na Javie 1.6

~ % javac RemoveListElementDemo.java
~ % java RemoveListElementDemo
~ % cat RemoveListElementDemo.java

import java.util.*;
public class RemoveListElementDemo {    
    private static final List<Integer> integerList;

    static {
        integerList = new ArrayList<Integer>();
        integerList.add(1);
        integerList.add(2);
        integerList.add(3);
    }

    public static void remove(Integer remove) {
        for(Integer integer : integerList) {
            if(integer.equals(remove)) {                
                integerList.remove(integer);
            }
        }
    }

    public static void main(String... args) {                
        remove(Integer.valueOf(2));

        Integer remove = Integer.valueOf(3);
        for(Integer integer : integerList) {
            if(integer.equals(remove)) {                
                integerList.remove(integer);
            }
        }
    }
}

~ %

 0
Author: battosai,
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
2012-07-12 18:09:38

W moim przypadku zrobiłem to tak:

int cursor = 0;
do {
    if (integer.equals(remove))
        integerList.remove(cursor);
    else cursor++;
} while (cursor != integerList.size());
 0
Author: Saif Hamed,
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-05 12:29:18

Zmień Iterator for each Na {[1] } aby rozwiązać.

A powodem jest:

Iteratory zwracane przez iteratora i listeratora tej klasy metody są Fail-fast: jeśli lista jest strukturalnie zmodyfikowana w dowolnym czasu po utworzeniu iteratora, w jakikolwiek sposób, z wyjątkiem własne metody usuwania lub dodawania iteratora, iterator wyrzuci ConcurrentModificationException.

--Referowane Dokumenty Java.

 0
Author: Stephen,
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-07 10:25:00

Sprawdź swój kod man....

W głównej metodzie próbujesz usunąć 4 element, którego nie ma, a co za tym idzie błąd. W metodzie remove() próbujesz usunąć trzeci element, który tam jest i stąd nie ma błędu.

 -1
Author: Abhishek,
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-22 13:07:04