it-swarm.com.de

Warum erhalte ich in diesem Beispiel keine Java.util.ConcurrentModificationException?

Hinweis: Mir ist die Methode Iterator#remove() bekannt.

Im folgenden Codebeispiel verstehe ich nicht, warum das List.remove in main Methode wirft ConcurrentModificationException, aber nicht in der remove Methode.

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);
            }
        }
    }
}
173
Bhesh Gurung

Hier ist der Grund: Wie es im Javadoc heißt:

Die von den Methoden iterator und listIterator dieser Klasse zurückgegebenen Iteratoren sind fehlerfrei: Wenn die Liste nach der Erstellung des Iterators zu einem beliebigen Zeitpunkt strukturell geändert wird, abgesehen von den eigenen Methoden remove oder add des Iterators, löst der Iterator eine ConcurrentModificationException aus.

Diese Überprüfung wird in der next() -Methode des Iterators durchgeführt (wie Sie am Stacktrace sehen können). Wir werden die next() -Methode jedoch nur erreichen, wenn hasNext() true liefert. Dies wird von jedem aufgerufen, um zu überprüfen, ob die Grenze erfüllt ist. Wenn in Ihrer remove-Methode hasNext() überprüft, ob ein anderes Element zurückgegeben werden muss, werden zwei Elemente zurückgegeben, und nachdem ein Element entfernt wurde, enthält die Liste nur zwei Elemente. Alles ist also pfirsichfarben und wir sind mit dem Iterieren fertig. Die Prüfung auf gleichzeitige Änderungen findet nicht statt, da dies in der Methode next() erfolgt, die niemals aufgerufen wird.

Als nächstes kommen wir zur zweiten Schleife. Nachdem wir die zweite Zahl entfernt haben, überprüft die hasNext-Methode erneut, ob weitere Werte zurückgegeben werden können. Es wurden bereits zwei Werte zurückgegeben, aber die Liste enthält nur noch einen. Aber der Code hier ist:

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

1! = 2, also fahren wir mit der next() -Methode fort, die jetzt erkennt, dass jemand mit der Liste rumgespielt hat und die Ausnahme auslöst.

Hoffe das klärt deine Frage auf.

Zusammenfassung

List.remove() wirft ConcurrentModificationException nicht, wenn das vorletzte Element aus der Liste entfernt wird.

258
pushy

Eine Möglichkeit, damit umzugehen, um ggf. etwas aus einer Kopie von Collection (nicht von Collection selbst) zu entfernen. Clone die ursprüngliche Sammlung, um eine Kopie über ein Constructor zu erstellen.

Diese Ausnahme kann von Methoden ausgelöst werden, die eine gleichzeitige Änderung eines Objekts festgestellt haben, wenn eine solche Änderung nicht zulässig ist.

In Ihrem speziellen Fall ist final meiner Meinung nach kein guter Weg, um die Liste nach der Deklaration zu ändern

private static final List<Integer> integerList;

Ziehen Sie auch in Betracht, eine Kopie anstelle der ursprünglichen Liste zu ändern.

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

for(Integer integer : integerList) {
    if(integer.equals(remove)) {                
        copy.remove(integer);
    }
}
42
JAM

Die Forward/Iterator-Methode funktioniert beim Entfernen von Elementen nicht. Sie können das Element ohne Fehler entfernen, es wird jedoch ein Laufzeitfehler angezeigt, wenn Sie versuchen, auf entfernte Elemente zuzugreifen. Sie können den Iterator nicht verwenden, da er, wie aufdringlich gezeigt wird, eine ConcurrentModificationException auslöst. Verwenden Sie stattdessen eine reguläre for-Schleife, gehen Sie jedoch zurück.

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);

Eine Lösung:

Durchsuchen Sie das Array in umgekehrter Reihenfolge, wenn Sie ein Listenelement entfernen möchten. Wenn Sie einfach die Liste rückwärts durchgehen, vermeiden Sie den Besuch eines entfernten Elements, wodurch die Ausnahme entfernt wird.

//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);
    }
}
13

Dieses Snippet löst immer eine ConcurrentModificationException aus.

Die Regel lautet "Sie dürfen keine Elemente in der Liste ändern (hinzufügen oder daraus entfernen), während Sie mit einem Iterator darüber iterieren (was bei Verwendung einer For-Each-Schleife der Fall ist)".

JavaDocs:

Die von den Methoden iterator und listIterator dieser Klasse zurückgegebenen Iteratoren sind fehlerfrei: Wenn die Liste nach dem Erstellen des Iterators zu einem beliebigen Zeitpunkt strukturell geändert wird, außer durch Entfernen oder Hinzufügen durch den Iterator Methoden löst der Iterator eine ConcurrentModificationException aus.

Wenn Sie die Liste (oder eine Sammlung im Allgemeinen) ändern möchten, verwenden Sie daher den Iterator, da dieser die Änderungen kennt und diese daher ordnungsgemäß behandelt werden.

Hoffe das hilft.

6
Bhushan

Ich hatte das gleiche Problem, aber für den Fall, dass ich ein Element in die iterierte Liste eingefügt habe. Ich habe es so gemacht

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);
        }
    }
}

Jetzt ist alles in Ordnung, da Sie keinen Iterator über Ihre Liste erstellen, sondern "manuell" darüber iterieren. Und die Bedingung i < integerList.size() täuscht Sie niemals, weil Sie beim Entfernen/Hinzufügen von Elementen zur Listengröße des Listeninkrements/-inkrements.

Hoffe es hilft, für mich war das die Lösung.

5
Gondil

Iterator ändern for each into for loop lösen.

nd der Grund ist:

Die von den Methoden iterator und listIterator dieser Klasse zurückgegebenen Iteratoren sind fehlerfrei: Wenn die Liste nach der Erstellung des Iterators zu einem beliebigen Zeitpunkt strukturell geändert wird, abgesehen von den eigenen Methoden remove oder add des Iterators, löst der Iterator eine ConcurrentModificationException aus.

- Weitergeleitet Java Docs.

1
Stephen

Dies läuft gut auf Java 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);
            }
        }
    }
}

~%

1
battosai

Wenn Sie Copy-on-Write-Sammlungen verwenden, funktioniert dies. Wenn Sie jedoch list.iterator () verwenden, verweist der zurückgegebene Iterator immer auf die Auflistung von Elementen wie zu dem Zeitpunkt, als (wie unten) list.iterator () aufgerufen wurde, auch wenn ein anderer Thread die Auflistung ändert. Alle Mutationsmethoden, die für einen schreibgeschützten Iterator oder ListIterator aufgerufen werden (z. B. add, set oder remove), lösen eine UnsupportedOperationException aus.

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
JohnnyO

In meinem Fall habe ich es so gemacht:

int cursor = 0;
do {
    if (integer.equals(remove))
        integerList.remove(cursor);
    else cursor++;
} while (cursor != integerList.size());
0
Saif Hamed