0Pricing
Java Academy · Lektion

Fail-Fast- vs. Fail-Safe-Iteratoren

Verstehen Sie ConcurrentModificationException und das Fail-Fast-Verhalten und erfahren Sie, wann Snapshot-Iteratoren sinnvoll sind.

Fail-Fast- vs. Fail-Safe-Iteratoren ist eine kostenlose Java Academy-Lektion auf CoddyKit. Dies ist Lektion 4 von 4. Du kannst die komplette Lektion unten kostenlos lesen – dann übst du sie direkt im Browser mit einem integrierten Code-Editor und einem KI-Tutor rund um die Uhr. Sie ist Teil des Java Academy-Lernpfads, und dein Fortschritt wird über Web und CoddyKit-App synchronisiert. Der Java Academy-Kurs umfasst insgesamt 4 Lektionen.

Fail-Fast- und Fail-Safe-Iteratoren

Java-Iteratoren werden anhand ihres Verhaltens kategorisiert, wenn die zugrunde liegende Collection während der Iteration geändert wird: Fail-Fast-Iteratoren lösen sofort eine Ausnahme aus; Fail-Safe-Iteratoren arbeiten mit einem Snapshot weiter.

Fail-Fast-Iteratoren

Die meisten standardmäßigen Java-Collections (ArrayList, HashMap, TreeSet) verwenden Fail-Fast-Iteratoren. Sie verfolgen einen modCount – einen Änderungszähler. Jede strukturelle Änderung während der Iteration löst ConcurrentModificationException aus.

import java.util.*;

List<String> list = new ArrayList<>(List.of("a", "b", "c"));

try {
    for (String s : list) {
        list.add("x"); // structural modification — throws!
    }
} catch (ConcurrentModificationException e) {
    System.out.println("ConcurrentModificationException caught!");
}
// Same for HashMap, TreeMap, HashSet, etc.

Warum es Fail-Fast gibt

Das Fail-Fast-Verhalten unterstützt das Debugging: Es macht Fehler sofort sichtbar, statt die Iteration mit einem beschädigten Zustand fortzusetzen. Die Alternative – eine unbemerkte Beschädigung – ist deutlich schwerer zu debuggen.

// modCount is incremented on every structural change
// (add, remove, clear on ArrayList)
// Iterator records modCount on creation
// On each next(), Iterator checks: if current modCount != expected, throw CME

// This detects bugs like:
List<Integer> nums = new ArrayList<>(List.of(1,2,3,4,5));
for (Integer n : nums) {
    if (n == 3) nums.remove(n); // bug caught immediately
}
// ConcurrentModificationException — not silent wrong results

Sicheres Entfernen: Iterator.remove()

Die einzige sichere Änderung während einer auf Iterator basierenden Iteration ist Iterator.remove() – sie entfernt das zuletzt zurückgegebene Element und aktualisiert modCount.

List<Integer> nums = new ArrayList<>(List.of(1,2,3,4,5,6));
Iterator<Integer> it = nums.iterator();

while (it.hasNext()) {
    int n = it.next();
    if (n % 2 == 0) it.remove(); // safe: updates modCount
}

System.out.println(nums); // [1, 3, 5]

removeIf: die moderne Alternative

Java 8 hat removeIf(Predicate) zu Collection hinzugefügt – eine übersichtlichere Möglichkeit, Elemente zu entfernen, die eine Bedingung erfüllen, ohne Iteratoren manuell verwalten zu müssen.

List<String> names = new ArrayList<>(List.of("Alice", "Bob", "Ann", "Charlie"));

// Modern: removeIf handles iteration internally
names.removeIf(name -> name.startsWith("A"));
System.out.println(names); // [Bob, Charlie]

// Equivalent but verbose iterator approach:
Iterator<String> it = names.iterator();
while (it.hasNext()) {
    if (it.next().startsWith("A")) it.remove();
}

Fail-Safe-Iteratoren: CopyOnWriteArrayList

CopyOnWriteArrayList verwendet einen Fail-Safe-Iterator. Er iteriert über einen Snapshot, der beim Erstellen des Iterators angelegt wurde – Änderungen beeinflussen die laufende Iteration nicht.

import java.util.concurrent.CopyOnWriteArrayList;

CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();
list.addAll(List.of("a", "b", "c"));

for (String s : list) {
    list.add("x"); // no ConcurrentModificationException!
    System.out.print(s + " "); // prints a, b, c (snapshot)
}
System.out.println();
System.out.println(list); // [a, b, c, x, x, x] — modified copy

ConcurrentHashMap-Iterator

ConcurrentHashMap verwendet schwach konsistente Iteratoren: Sie spiegeln den Zustand zu einem beliebigen Zeitpunkt während oder nach der Erstellung des Iterators wider, lösen keine CME aus und sehen möglicherweise Änderungen durch andere Threads oder auch nicht.

import java.util.concurrent.ConcurrentHashMap;

ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();
map.put("a", 1); map.put("b", 2); map.put("c", 3);

for (Map.Entry<String, Integer> e : map.entrySet()) {
    map.put("d", 4); // no CME — weakly consistent
    System.out.println(e.getKey() + "=" + e.getValue());
}
// May or may not print "d=4" — weakly consistent

Collections.synchronizedList

Collections.synchronizedList ist NICHT Fail-Safe – die Methode verwendet weiterhin Fail-Fast-Iteratoren, blockiert aber den gleichzeitigen Zugriff. Sie müssen während der Iteration manuell synchronisieren.

import java.util.*;

List<String> syncList = Collections.synchronizedList(new ArrayList<>());
syncList.addAll(List.of("a", "b", "c"));

// Must synchronize during iteration!
synchronized (syncList) {
    for (String s : syncList) {
        System.out.println(s);
    }
}
// Without the synchronized block, CME is still possible from another thread

Snapshot-Iterator-Muster

Erstellen Sie manuell einen Snapshot, um sicher über eine veränderliche Collection zu iterieren, ohne Synchronisierung zu verwenden.

import java.util.*;

List<String> original = new ArrayList<>(List.of("a", "b", "c"));

// Take a snapshot copy before iterating
List<String> snapshot = List.copyOf(original);

for (String s : snapshot) {
    // Safe to modify original during snapshot iteration
    original.remove(s);
    System.out.println("Removed: " + s);
}
System.out.println(original); // []

Vor- und Nachteile von CopyOnWrite

CopyOnWriteArrayList ist Fail-Safe, aber bei Schreibvorgängen teuer. Jede Änderung erstellt eine vollständige Kopie.

  • Geeignet für: viele Lesevorgänge, seltene Schreibvorgänge (Listen von Event-Listenern)
  • Ungeeignet für: häufige Änderungen, große Collections

Zusammenfassung: Fail-Fast und Fail-Safe

Zusammenfassung des Vergleichs:

  • Fail-Fast: ArrayList, HashMap, TreeMap – lösen bei Änderungen eine CME aus und prüfen modCount
  • Fail-Safe/schwach konsistent: ConcurrentHashMap, CopyOnWriteArrayList – keine CME, Iteration über einen Snapshot oder über gleichzeitig veränderte Daten
  • Sichere Änderungsmethoden: Iterator.remove(), removeIf(), replaceAll()

Praxis: Abgelaufene Sitzungen bereinigen

Abgelaufene Sitzungen mithilfe geeigneter Muster sicher aus einer nebenläufigen Liste entfernen.

import java.util.*;
import java.util.concurrent.*;

class SessionManager {
    private final CopyOnWriteArrayList<Session> sessions = new CopyOnWriteArrayList<>();

    record Session(String id, long expiresAt) {
        boolean isExpired() { return System.currentTimeMillis() > expiresAt; }
    }

    void add(Session s) { sessions.add(s); }

    // Safe to call from multiple threads while iterating
    void purgeExpired() {
        sessions.removeIf(Session::isExpired);
    }

    List<Session> active() {
        return sessions.stream().filter(s -> !s.isExpired()).toList();
    }
}

Schnelltest

Welche Ausnahme wird ausgelöst, wenn Sie eine ArrayList während der Iteration ändern?

Zusammenfassung: Fail-Fast- und Fail-Safe-Iteratoren

Die wichtigsten Erkenntnisse:

  • Fail-Fast-Iteratoren (ArrayList, HashMap) lösen bei gleichzeitigen Änderungen ConcurrentModificationException aus
  • Fail-Safe-Iteratoren (CopyOnWriteArrayList, ConcurrentHashMap) erlauben Änderungen, ohne eine Ausnahme auszulösen
  • Iterator.remove() ist die einzige sichere Möglichkeit, während einer Fail-Fast-Iteration Elemente zu entfernen
  • removeIf() ist die moderne, übersichtlichere Alternative zu Iterator.remove()
  • CopyOnWriteArrayList: sicher für Szenarien mit vielen Lese- und wenigen Schreibvorgängen
  • ConcurrentHashMap: schwach konsistent – keine CME; gleichzeitig vorgenommene Änderungen werden möglicherweise erkannt oder auch nicht

Häufig gestellte Fragen

Ist die Lektion „Fail-Fast- vs. Fail-Safe-Iteratoren“ kostenlos?

Ja — der vollständige Text von „Fail-Fast- vs. Fail-Safe-Iteratoren“ ist hier im Web kostenlos zu lesen. Um sie interaktiv zu üben (integrierter Code-Editor und 24/7 KI-Tutor) und den Rest des Java Academy-Kurses freizuschalten, upgrade auf CoddyKit PRO. Der Java Academy-Kurs umfasst insgesamt 4 Lektionen.

Was lerne ich in „Fail-Fast- vs. Fail-Safe-Iteratoren“?

Verstehen Sie ConcurrentModificationException und das Fail-Fast-Verhalten und erfahren Sie, wann Snapshot-Iteratoren sinnvoll sind. Du übst Java Academy mit praktischem Code, den du direkt im Browser ausführst, und ein 24/7 KI-Tutor beantwortet deine Fragen während du die Lektion bearbeitest.

Brauche ich Erfahrung, um Java Academy zu starten?

Keine Vorkenntnisse erforderlich. Java Academy auf CoddyKit ist für Anfänger bis fortgeschrittene Lernende strukturiert, sodass du hier starten oder von Anfang an beginnen und in deinem eigenen Tempo voranschreiten kannst. Dies ist Lektion 4 von 4.

Wie lange dauert die Lektion „Fail-Fast- vs. Fail-Safe-Iteratoren“?

Die meisten CoddyKit-Lektionen dauern etwa 5–10 Minuten. Jede ist kompakt und interaktiv, sodass du stetig Fortschritte machst und genau dort weitermachst, wo du aufgehört hast – im Web und in der App.

Kann ich in dieser Java Academy-Lektion Code schreiben und ausführen?

Ja. Jede Java Academy-Lektion enthält einen integrierten Code-Editor, sodass du echten Code direkt in deinem Browser schreibst und ausführst und sofort KI-Feedback erhältst — ohne lokale Einrichtung erforderlich.

Alle Lektionen in diesem Kurs

  1. Die Verträge von Iterable und Iterator
  2. Einen eigenen Iterator implementieren
  3. ListIterator und bidirektionales Durchlaufen
  4. Fail-Fast- vs. Fail-Safe-Iteratoren
← Zurück zu Java Academy