Vermeiden der ConcurrentModificationException in Java

1. Einleitung

In diesem Artikel werfen wir einen Blick auf die ConcurrentModificationException- Klasse.

Zuerst geben wir eine Erklärung, wie es funktioniert, und beweisen es dann anhand eines Tests zum Auslösen.

Abschließend werden wir anhand praktischer Beispiele einige Problemumgehungen ausprobieren.

2. Auslösen einer ConcurrentModificationException

Im Wesentlichen wird die ConcurrentModificationException verwendet, um schnell zu versagen, wenn etwas geändert wird, auf dem wir iterieren. Lassen Sie uns dies mit einem einfachen Test beweisen:

@Test(expected = ConcurrentModificationException.class) public void whilstRemovingDuringIteration_shouldThrowException() throws InterruptedException { List integers = newArrayList(1, 2, 3); for (Integer integer : integers) { integers.remove(1); } }

Wie wir sehen können, entfernen wir vor Abschluss unserer Iteration ein Element. Das löst die Ausnahme aus.

3. Lösungen

Manchmal möchten wir möglicherweise tatsächlich Elemente aus einer Sammlung entfernen, während Sie iterieren. Wenn dies der Fall ist, gibt es einige Lösungen.

3.1. Iterator direkt verwenden

Eine for-each- Schleife verwendet einen Iterator hinter den Kulissen, ist jedoch weniger ausführlich. Wenn wir jedoch unseren vorherigen Test auf die Verwendung eines Iterators umgestaltet haben , haben wir Zugriff auf zusätzliche Methoden, wie z. B. remove (). Versuchen wir stattdessen, mit dieser Methode unsere Liste zu ändern:

for (Iterator iterator = integers.iterator(); iterator.hasNext();) { Integer integer = iterator.next(); if(integer == 2) { iterator.remove(); } }

Jetzt werden wir feststellen, dass es keine Ausnahme gibt. Der Grund dafür ist, dass die remove () -Methode keine ConcurrentModificationException verursacht. Es ist sicher, während der Iteration aufzurufen.

3.2. Während der Iteration nicht entfernen

Wenn wir unsere for-each- Schleife behalten wollen, können wir das. Es ist nur so, dass wir bis nach der Iteration warten müssen, bevor wir die Elemente entfernen. Probieren wir dies aus, indem wir das, was wir entfernen möchten, während der Iteration zu einer toRemove- Liste hinzufügen :

List integers = newArrayList(1, 2, 3); List toRemove = newArrayList(); for (Integer integer : integers) { if(integer == 2) { toRemove.add(integer); } } integers.removeAll(toRemove); assertThat(integers).containsExactly(1, 3); 

Dies ist ein weiterer effektiver Weg, um das Problem zu umgehen.

3.3. Verwenden von removeIf ()

Java 8 führte die Methode removeIf () in die Collection- Schnittstelle ein. Das heißt, wenn wir damit arbeiten, können wir Ideen der funktionalen Programmierung verwenden, um wieder dieselben Ergebnisse zu erzielen:

List integers = newArrayList(1, 2, 3); integers.removeIf(i -> i == 2); assertThat(integers).containsExactly(1, 3);

Dieser deklarative Stil bietet uns die geringste Ausführlichkeit. Abhängig vom Anwendungsfall finden wir jedoch möglicherweise andere Methoden bequemer.

3.4. Filtern mit Streams

Wenn wir in die Welt der funktionalen / deklarativen Programmierung eintauchen, können wir das Mutieren von Sammlungen vergessen. Stattdessen können wir uns auf Elemente konzentrieren, die tatsächlich verarbeitet werden sollten:

Collection integers = newArrayList(1, 2, 3); List collected = integers .stream() .filter(i -> i != 2) .map(Object::toString) .collect(toList()); assertThat(collected).containsExactly("1", "3");

Wir haben das Gegenteil zu unserem vorherigen Beispiel getan, indem wir ein Prädikat zum Bestimmen von Elementen bereitgestellt haben, die eingeschlossen und nicht ausgeschlossen werden sollen. Der Vorteil ist, dass wir neben dem Entfernen auch andere Funktionen miteinander verketten können. Im Beispiel verwenden wir eine funktionale Map (), können aber noch mehr Operationen verwenden, wenn wir möchten.

4. Fazit

In diesem Artikel haben wir Probleme aufgezeigt, die auftreten können, wenn Sie während der Iteration Elemente aus einer Sammlung entfernen, und einige Lösungen bereitgestellt, um das Problem zu beheben.

Die Implementierung dieser Beispiele finden Sie auf GitHub. Dies ist ein Maven-Projekt und sollte daher so wie es ist einfach auszuführen sein.