1301 Stimmen

Iteration durch eine Sammlung, Vermeidung von ConcurrentModificationException beim Entfernen von Objekten in einer Schleife

Wir alle wissen, dass Sie Folgendes nicht tun können, weil ConcurrentModificationException :

for (Object i : l) {
    if (condition(i)) {
        l.remove(i);
    }
}

Aber das funktioniert offenbar manchmal, aber nicht immer. Hier ist ein spezieller Code:

public static void main(String[] args) {
    Collection<Integer> l = new ArrayList<>();

    for (int i = 0; i < 10; ++i) {
        l.add(4);
        l.add(5);
        l.add(6);
    }

    for (int i : l) {
        if (i == 5) {
            l.remove(i);
        }
    }

    System.out.println(l);
}

Das führt natürlich dazu, dass:

Exception in thread "main" java.util.ConcurrentModificationException

Auch wenn mehrere Threads dies nicht tun. Wie auch immer.

Was ist die beste Lösung für dieses Problem? Wie kann ich ein Element aus der Sammlung in einer Schleife entfernen, ohne diese Ausnahme auszulösen?

Ich verwende auch eine willkürliche Collection hier, nicht unbedingt ein ArrayList Sie können sich also nicht auf get .

1 Stimmen

Hinweis an die Leser: Lesen Sie unbedingt die docs.oracle.com/javase/tutorial/collections/interfaces/ kann es einen einfacheren Weg geben, um das zu erreichen, was Sie tun wollen.

0voto

Yazon2006 Punkte 3526

Beispiel für die Änderung einer thread-sicheren Sammlung:

public class Example {
    private final List<String> queue = Collections.synchronizedList(new ArrayList<String>());

    public void removeFromQueue() {
        synchronized (queue) {
            Iterator<String> iterator = queue.iterator();
            String string = iterator.next();
            if (string.isEmpty()) {
                iterator.remove();
            }
        }
    }
}

0voto

cellepo Punkte 3302

Ich weiß, dass diese Frage nur eine Vermutung ist. Collection und nicht speziell eine List . Aber für diejenigen, die diese Frage lesen und tatsächlich mit einer List Referenz, können Sie vermeiden ConcurrentModificationException mit einer while -Schleife (während Sie darin Änderungen vornehmen), wenn Sie vermeiden wollen, dass Iterator (entweder wenn Sie es generell vermeiden wollen, oder wenn Sie es speziell vermeiden wollen, um eine Schleifenreihenfolge zu erreichen, die nicht von Anfang bis Ende bei jedem Element anhält [was meiner Meinung nach die einzige Reihenfolge ist Iterator selbst tun kann]):

*Aktualisierung: Siehe Kommentare weiter unten, die verdeutlichen, dass das Gleiche auch mit dem traditionell -for-Schleife.

final List<Integer> list = new ArrayList<>();
for(int i = 0; i < 10; ++i){
    list.add(i);
}

int i = 1;
while(i < list.size()){
    if(list.get(i) % 2 == 0){
        list.remove(i++);

    } else {
        i += 2;
    }
}

Keine ConcurrentModificationException in diesem Code.

Hier sehen wir, dass die Schleifenbildung nicht am Anfang beginnt und nicht bei jede Element (das meiner Meinung nach Iterator selbst nicht tun kann).

FWIW wir sehen auch get aufrufend list was nicht möglich wäre, wenn die Referenz nur Collection (anstelle des spezifischeren List -Typ von Collection ) - List Schnittstelle umfasst get pero Collection Schnittstelle nicht. Wäre dieser Unterschied nicht, dann würde die list Referenz könnte stattdessen eine Collection [und somit wäre diese Antwort technisch gesehen eine direkte Antwort, anstatt einer tangentialen Antwort].

FWIWW derselbe Code funktioniert immer noch, nachdem er so geändert wurde, dass er am Anfang beginnt und bei jedem Element aufhört (genau wie Iterator Ordnung):

final List<Integer> list = new ArrayList<>();
for(int i = 0; i < 10; ++i){
    list.add(i);
}

int i = 0;
while(i < list.size()){
    if(list.get(i) % 2 == 0){
        list.remove(i);

    } else {
        ++i;
    }
}

0 Stimmen

Dies erfordert jedoch immer noch eine sehr sorgfältige Berechnung der zu entfernenden Indikatoren.

0 Stimmen

Außerdem ist dies nur eine ausführlichere Erklärung dieser Antwort stackoverflow.com/a/43441822/2308683

0 Stimmen

Gut zu wissen - danke! Diese andere Antwort half mir zu verstehen, dass es die erweitert -for-Schleife, die Folgendes auslösen würde ConcurrentModificationException aber no die traditionell -for-Schleife (die in der anderen Antwort verwendet wird) - ohne das vorher zu wissen, war ich motiviert, diese Antwort zu schreiben (ich dachte damals irrtümlich, dass es sich um tous for-Schleifen, die die Exception auslösen würden).

0voto

Rahul Vala Punkte 583

Eine Lösung könnte darin bestehen, die Liste zu drehen und das erste Element zu entfernen, um die ConcurrentModificationException oder IndexOutOfBoundsException zu vermeiden

int n = list.size();
for(int j=0;j<n;j++){
    //you can also put a condition before remove
    list.remove(0);
    Collections.rotate(list, 1);
}
Collections.rotate(list, -1);

0voto

Oleg Tatarchuk Punkte 91

Versuchen Sie dies (entfernt alle Elemente in der Liste, die gleich i ):

for (Object i : l) {
    if (condition(i)) {
        l = (l.stream().filter((a) -> a != i)).collect(Collectors.toList());
    }
}

0voto

Oguzhan Cevik Punkte 486

Sie können eine while-Schleife verwenden.

Iterator<Map.Entry<String, String>> iterator = map.entrySet().iterator();
while(iterator.hasNext()){
    Map.Entry<String, String> entry = iterator.next();
    if(entry.getKey().equals("test")) {
        iterator.remove();
    } 
}

0 Stimmen

Es geht hier nicht um die while Schleife, sondern das Entfernen über die Iterator.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X