Fail-Fast vs Fail-Safe Iterators in Java
Iterators in Java behave differently when the underlying collection is modified during iteration. This difference creates two important types:

- Fail-Fast Iterators
- Fail-Safe Iterators
Understanding these behaviors is very important for interviews and real-world multithreaded applications.
Fail-Fast Iterators
A Fail-Fast Iterator immediately throws a ConcurrentModificationException if the collection is structurally modified during iteration (other than using iterator.remove()).
Found In (Non-Concurrent Collections)
Fail-fast iterators are used in normal Java collections:
- ArrayList
- LinkedList
- HashSet
- LinkedHashSet
- TreeSet
- HashMap (via keySet(), values(), entrySet())
- LinkedHashMap
- TreeMap
- Vector (modern iterator)
These belong to the java.util package.
How Fail-Fast Works Internally
Fail-fast iterators use two variables:
modCount→ tracks structural changes in the collectionexpectedModCount→ value stored by iterator at the start
When iterating:
If (modCount != expectedModCount)
throw ConcurrentModificationException
So any structural modification during iteration breaks synchronization and throws an exception.
Fail-Fast Example (Throws Exception)
import java.util.*;
public class Main {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("A");
list.add("B");
list.add("C");
for (String s : list) {
if (s.equals("B")) {
list.remove(s); // Structural modification
}
}
}
}
Output:
Exception in thread "main" java.util.ConcurrentModificationException
How to Use Fail-Fast Safely
Use iterator.remove(), not list.remove():
Iterator<String> it = list.iterator();
while (it.hasNext()) {
String s = it.next();
if (s.equals("B")) {
it.remove(); // Safe
}
}
This avoids fail-fast exception because removal happens through the iterator itself.
Characteristics of Fail-Fast
- Throws
ConcurrentModificationException - Faster (no copy is created)
- Not thread-safe
- Unsafe during concurrent modifications
- Used in almost all non-concurrent collections
2. Fail-Safe Iterators
A Fail-Safe Iterator does not throw exceptions even if the collection is modified during iteration.
Why?
Because it works on a copy (snapshot) of the underlying collection.
Found In (Concurrent Collections)
Fail-safe iterators come from the java.util.concurrent package:
- ConcurrentHashMap
- CopyOnWriteArrayList
- CopyOnWriteArraySet
- ConcurrentSkipListMap
- ConcurrentSkipListSet
These collections are designed for multithreading.
How Fail-Safe Works Internally
- Creates a clone or snapshot copy of the collection
- Iterator works on the copy, not the original
- Original collection can be safely modified during iteration
- Does not throw
ConcurrentModificationException - Changes made during iteration are NOT visible to the iterator
Fail-Safe Example (No Exception)
import java.util.concurrent.*;
public class Main {
public static void main(String[] args) {
CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();
list.add("A");
list.add("B");
for (String s : list) {
list.add("C"); // No exception
}
System.out.println(list);
}
}
Output:
[A, B, C, C]
Characteristics of Fail-Safe
- No
ConcurrentModificationException - Safe in multi-threaded environments
- Allows modification during iteration
- Slower due to copying
- The iterator does not reflect new changes
- Thread-safe
Fail-Fast vs Fail-Safe Iterators
| Feature | Fail-Fast Iterator | Fail-Safe Iterator |
|---|---|---|
| Works on | Original collection | Copy of collection |
| Throws exception? | Yes, ConcurrentModificationException | No |
| Safety | Not thread-safe | Thread-safe |
| Performance | Faster | Slower (due to copy) |
| Used in | Normal Collections | Concurrent Collections |
Diagram: Fail-Fast vs Fail-Safe
Fail-Fast
Original Collection
↓
Iterator (checks modCount)
↓
Modification → Exception
Fail-Safe
Original Collection → Snapshot Copy
↓
Iterator
(Modifications allowed, no exception)
Points to Remember
- Fail-Fast = checks modification immediately
- Fail-Safe = works on duplicate copy
- Fail-Fast is common in List, Set, Map
- Fail-Safe is common in Concurrent collections
- Using
iterator.remove()prevents fail-fast errors - Fail-Fast is not guaranteed — it is “best-effort”
