Iterator fail-fast e fail-safe
Comprenda ConcurrentModificationException, il comportamento fail-fast e quando utilizzare iteratori basati su snapshot.
Iterator fail-fast e fail-safe è una lezione Java Academy gratuita su CoddyKit. Questa è la lezione 4 di 4. Puoi leggere la lezione completa qui gratuitamente — poi esercitati direttamente nel browser con un editor di codice integrato e un tutor IA disponibile 24/7. Fa parte del percorso di apprendimento Java Academy, e i tuoi progressi si sincronizzano tra il web e l'app CoddyKit. Il corso Java Academy include 4 lezioni in totale.
Iterator fail-fast e fail-safe
Gli iterator Java vengono classificati in base al loro comportamento quando la collezione sottostante viene modificata durante l'iterazione: gli iterator fail-fast generano immediatamente un'eccezione; quelli fail-safe continuano a operare su uno snapshot.
Iterator fail-fast
La maggior parte delle collezioni Java standard (ArrayList, HashMap, TreeSet) utilizza iterator fail-fast. Questi tengono traccia di un modCount, ovvero un contatore delle modifiche. Qualsiasi modifica strutturale durante l'iterazione genera ConcurrentModificationException.
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.Perché esiste il comportamento fail-fast
Il comportamento fail-fast è uno strumento di debug: porta immediatamente alla luce gli errori invece di consentire all'iterazione di continuare con uno stato corrotto. L'alternativa, ovvero una corruzione silenziosa, è molto più difficile da analizzare.
// 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 resultsRimozione sicura: Iterator.remove()
L'unica modifica sicura durante un'iterazione basata su Iterator è Iterator.remove(): rimuove l'ultimo elemento restituito e aggiorna 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: l'alternativa moderna
Java 8 ha aggiunto removeIf(Predicate) a Collection: un modo più pulito per rimuovere gli elementi che soddisfano una condizione senza gestire manualmente l'iterator.
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();
}Iterator fail-safe: CopyOnWriteArrayList
CopyOnWriteArrayList utilizza un iterator fail-safe. L'iterazione avviene su uno snapshot creato al momento della creazione dell'iterator: le modifiche non influiscono sull'iterazione in corso.
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 copyIterator di ConcurrentHashMap
ConcurrentHashMap utilizza iterator debolmente coerenti: riflettono lo stato in un momento qualsiasi durante o dopo la loro creazione, non generano CME e possono vedere o non vedere gli aggiornamenti concorrenti.
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 consistentCollections.synchronizedList
Collections.synchronizedList NON è fail-safe: utilizza comunque iterator fail-fast, ma blocca l'accesso concorrente. Deve sincronizzare manualmente l'iterazione.
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 threadPattern dell'iterator su snapshot
Crei manualmente uno snapshot per iterare in sicurezza su una collezione modificabile senza sincronizzazione.
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); // []Compromessi di CopyOnWrite
CopyOnWriteArrayList è fail-safe, ma le scritture sono costose. Ogni modifica crea una copia completa.
- Adatto per: molte letture e scritture rare (liste di listener di eventi)
- Non adatto per: modifiche frequenti e collezioni di grandi dimensioni
Riepilogo: fail-fast e fail-safe
Riepilogo del confronto:
- Fail-fast: ArrayList, HashMap, TreeMap; generano CME in caso di modifica e controllano modCount
- Fail-safe/debolmente coerenti: ConcurrentHashMap, CopyOnWriteArrayList; non generano CME e iterano su uno snapshot o sui dati concorrenti
- Metodi di modifica sicuri: Iterator.remove(), removeIf(), replaceAll()
Applicazione pratica: pulizia delle sessioni scadute
Rimuovere in sicurezza le sessioni scadute da una lista concorrente utilizzando i pattern appropriati.
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();
}
}Verifica rapida
Quale eccezione genera la modifica di un ArrayList durante l'iterazione?
Riepilogo: iterator fail-fast e fail-safe
Punti chiave:
- Gli iterator fail-fast (ArrayList, HashMap) generano ConcurrentModificationException in caso di modifica concorrente
- Gli iterator fail-safe (CopyOnWriteArrayList, ConcurrentHashMap) consentono le modifiche senza generare eccezioni
- Iterator.remove() è l'unico modo sicuro per rimuovere elementi durante un'iterazione fail-fast
- removeIf() è l'alternativa moderna e più pulita a Iterator.remove() manuale
- CopyOnWriteArrayList: sicuro negli scenari con molte letture e poche scritture
- ConcurrentHashMap: debolmente coerente; non genera CME e può vedere o non vedere le modifiche concorrenti
Impara Java con un tutor IA — gratis
Scrivi ed esegui vero codice nel tuo browser, ricevi aiuto istantaneo da un tutor IA disponibile 24/7, e riprendi da dove hai lasciato sul web o nell'app.
- Corsi
- 104
- Lezioni
- 374
Domande Frequenti
La lezione «Iterator fail-fast e fail-safe» è gratuita?
Sì — il testo completo di «Iterator fail-fast e fail-safe» è gratuito qui sul web. Per esercitarvi in modo interattivo (un editor di codice integrato e un tutor IA 24/7) e sbloccare il resto del corso Java Academy, passa a CoddyKit PRO. Il corso Java Academy include 4 lezioni in totale.
Cosa imparerò in «Iterator fail-fast e fail-safe»?
Comprenda ConcurrentModificationException, il comportamento fail-fast e quando utilizzare iteratori basati su snapshot. Eserciti Java Academy con codice pratico che esegui direttamente nel browser, e un tutor IA 24/7 risponde alle tue domande mentre lavori sulla lezione.
Ho bisogno di esperienza per iniziare Java Academy?
Non è richiesta alcuna esperienza precedente. Java Academy su CoddyKit è strutturato per principianti e studenti avanzati, quindi puoi iniziare da qui o dall'inizio e procedere al tuo ritmo. Questa è la lezione 4 di 4.
Quanto tempo richiede la lezione «Iterator fail-fast e fail-safe»?
La maggior parte delle lezioni CoddyKit richiede circa 5–10 minuti. Ogni lezione è breve e interattiva, quindi fai progressi costanti e riprendi esattamente da dove hai lasciato su web e app.
Posso scrivere ed eseguire codice in questa lezione Java Academy?
Sì. Ogni lezione Java Academy include un editor di codice integrato, quindi scrivi ed esegui codice reale direttamente nel tuo browser e ricevi feedback istantaneo dall'IA — nessuna configurazione locale necessaria.
Tutte le lezioni di questo corso
- I contratti di Iterable e Iterator
- Implementazione di un Iterator personalizzato
- ListIterator e attraversamento bidirezionale
- Iterator fail-fast e fail-safe