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.

10voto

from56 Punkte 3695

Mit einer traditionellen for-Schleife

ArrayList<String> myArray = new ArrayList<>();

for (int i = 0; i < myArray.size(); ) {
    String text = myArray.get(i);
    if (someCondition(text))
        myArray.remove(i);
    else
        i++;   
}

1 Stimmen

Ah, es ist also wirklich nur die erweitert -for-Schleife, die die Exception auslöst.

0 Stimmen

FWIW - derselbe Code würde auch noch funktionieren, nachdem er so geändert wurde, dass er inkrementiert wird i++ im Schleifenschutz und nicht im Schleifenkörper.

0 Stimmen

Berichtigung ^: Das heißt, wenn die i++ Die Inkrementierung war nicht bedingt - ich verstehe jetzt, warum man es im Körper macht :)

5voto

Yessy Punkte 888

ConcurrentHashMap o ConcurrentLinkedQueue o ConcurrentSkipListMap können eine weitere Option sein, da sie nie eine ConcurrentModificationException auslösen, selbst wenn Sie Elemente entfernen oder hinzufügen.

0 Stimmen

Ja, und beachten Sie, dass diese alle in java.util.concurrent Paket. Einige andere ähnliche/gemeinsam genutzte Klassen aus diesem Paket sind CopyOnWriteArrayList & CopyOnWriteArraySet [aber nicht auf diese beschränkt].

1 Stimmen

Tatsächlich habe ich gerade erfahren, dass diese Datenstruktur Objects vermeiden ConcurrentModificationException , die sie in einem erweitert -for-Schleife kann immer noch zu Indizierungsproblemen führen (d. h., es werden immer noch Elemente übersprungen, oder IndexOutOfBoundsException ...)

4voto

Nestor Milyaev Punkte 4616

Eine andere Möglichkeit ist, eine Kopie der arrayList nur für die Iteration zu verwenden:

List<Object> l = ...

List<Object> iterationList = ImmutableList.copyOf(l);

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

2 Stimmen

Nota: i ist kein index sondern das Objekt. Vielleicht rufen Sie es obj wäre passender.

1 Stimmen

Dies wurde bereits 2012 oben vorgeschlagen: stackoverflow.com/a/11201224/3969362 Eine Kopie der Liste zu erstellen ist das, was sie normalerweise mit Hörern auf Android tun. Es ist eine gültige Lösung für kleine Listen.

3voto

james.garriss Punkte 12128

A ListIterator können Sie Elemente in der Liste hinzufügen oder entfernen. Angenommen, Sie haben eine Liste mit Car Objekte:

List<Car> cars = ArrayList<>();
// add cars here...

for (ListIterator<Car> carIterator = cars.listIterator();  carIterator.hasNext(); )
{
   if (<some-condition>)
   { 
      carIterator().remove()
   }
   else if (<some-other-condition>)
   { 
      carIterator().add(aNewCar);
   }
}

0 Stimmen

Die zusätzlichen Methoden in der ListIterator Schnittstelle (Erweiterung von Iterator) sind interessant - insbesondere die previous Methode.

2voto

Adil Karaöz Punkte 196

Jetzt können Sie mit folgendem Code entfernen

l.removeIf(current -> current == 5);

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