A fail-fast iterator throws ConcurrentModificationException as soon as it detects that the collection was structurally changed (an element added or removed) by anything other than the iterator itself. A fail-safe iterator works on a copy or a weakly consistent view, so it never throws that exception, but it may not show the latest changes.
Key Points: • Fail-fast: the iterators of ArrayList, HashMap, HashSet and most java.util collections. They track a modification count (modCount) and check it on every next(). • Fail-safe: the iterators of CopyOnWriteArrayList and ConcurrentHashMap (java.util.concurrent). • CopyOnWriteArrayList iterates over a snapshot taken when the iterator was created. ConcurrentHashMap's iterator is weakly consistent: it may or may not reflect changes made during iteration. • Fail-fast behaviour is "best effort": it helps you find bugs, but you should not rely on it for correctness. • Removing through the iterator itself (iterator.remove()) is allowed and does not trigger the exception.
Code Example:
import java.util.ArrayList;
import java.util.ConcurrentModificationException;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
public class FailFastVsFailSafe {
public static void main(String[] args) {
List<String> failFast = new ArrayList<>(List.of("A", "B", "C"));
try {
for (String s : failFast) {
if (s.equals("A")) failFast.add("D"); // structural change
}
} catch (ConcurrentModificationException e) {
System.out.println("ArrayList: ConcurrentModificationException");
}
List<String> failSafe = new CopyOnWriteArrayList<>(List.of("A", "B", "C"));
for (String s : failSafe) {
if (s.equals("A")) failSafe.add("D"); // iterator uses a snapshot
System.out.print(s + " ");
}
System.out.println();
System.out.println("After loop: " + failSafe);
}
}Output:
ArrayList: ConcurrentModificationException A B C After loop: [A, B, C, D]
Good to Know: A common follow-up is "How do you remove elements while iterating?" Use iterator.remove(), or list.removeIf(condition) from Java 8, which is the cleanest option. Never call list.remove() inside a for-each loop.
Interview Tip: A concise interview answer is:
"A fail-fast iterator, like those of ArrayList and HashMap, throws ConcurrentModificationException when the collection is structurally modified during iteration, using a modification counter. A fail-safe iterator, like those of CopyOnWriteArrayList and ConcurrentHashMap, works on a snapshot or a weakly consistent view, so it doesn't throw, but it may not see the latest changes."
Previous question: What is the best way to iterate over a HashMap in Java?
Next question: How do you avoid ConcurrentModificationException when removing elements from a list?