Java – for-each rettet einen

Heute sollte ich einen Fehler im fremden Code heraus finden. Das Java-Programm warf folgende Exception aus:

java.util.ConcurrentModificationException

Tja, es wurde auf jeden Fall von einer ArrayList-Collection geworfen. Meine erste Vermutung war, das zwei Threads auf dieser ArrayList schreiben. Also auf den thread-sicheren Vector umsteigen? Hat aber nichts geholfen.

Gut, also den Callstack noch mal genau durch suchen. Und siehe da, eine for-each-Schleife ist schuld. Sie arbeitet die Collection durch und irgendwo in den tiefen des Algorithmus wird ein Element aus der Collection mit remove entfernt.

Was ist passiert? Ein Beispiel:

public class InvalidIterator {
    static List<String> list = new ArrayList<>();

    public static void foo(String s) {
        System.out.println(s);
        list.remove(s);
    }

    public static void main(String[] args) {
        list.add("A");
        list.add("B");
        list.add("C");

        for (String s : list) {
            foo(s);
        }
    }
}

Die for-each-Schleife arbeitet mit Iteratoren der Collections. In Zeile 6 wird das Element s entfernt. Bei der nächsten Iteration in Zeile 14 wird ein ungültiger Iterator verwendet, weil das Element nicht mehr existiert und die weitere Iteration scheitert. BOOM!

Bei einer klassischen for-Schleife mit Indexierung:

for (int i = 0; i < list.size(); i++) {
  foo(list.get(i));
}

würde gar keine Exception geworfen werden. Achtung Falle: Es würde aber nur jedes zweite Element bearbeitet werden, da die nächsten Elemente auf den frei gewordenen Platz vorrücken und der Index zusätzlich inkrementiert!

Was ist in dem Fall besser? Natürlich die Variante mit for-each und einer Exception! Mit dem Index-Zugriff präsentiert man dem Anwender angeblich korrekte Daten. Da ist eine Exception und Abbruch ohne fehlerhafte Daten doch besser?!

Lösung

Die Lösung ist einfach: Sobald man in einer Schleife Elemente aus einer Collection entfernen will, sollte man diese rückwärts abarbeiten.

for (int i = list.size() - 1; i >= 0; i--) {
  foo(list.get(i));
}

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.

Blue Captcha Image Refresh

*