Fail-Fast and Fail-Safe are two approaches that Java collections use to handle concurrent modifications when iterating over elements. Here’s an in-depth comparison between the two with examples to illustrate their behavior.
Fail-Fast Iterator
A fail-fast iterator immediately throws a ConcurrentModificationException
if a collection is modified structurally after the iterator is created (except through the iterator itself). Fail-fast iterators operate directly on the collection and detect changes by checking a modification count.
- Characteristics:
- Directly iterates over the collection.
- Throws
ConcurrentModificationException
if the collection is structurally modified (addition or removal of elements) after the iterator’s creation. - Example collections:
ArrayList
,HashMap
,HashSet
.
Example of Fail-Fast Iterator
import java.util.ArrayList;
import java.util.Iterator;
public class FailFastExample {
public static void main(String[] args) {
ArrayList list = new ArrayList<>(List.of(1, 2, 3, 4));
Iterator iterator = list.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
list.add(5); // Modifying the collection while iterating
}
}
}
Output:
1
Exception in thread "main" java.util.ConcurrentModificationException
In this example:
- The iterator is traversing the
ArrayList
, but when we try to add an element during iteration, the mod count is updated in theArrayList
. - The iterator detects this structural modification and throws a
ConcurrentModificationException
.
Fail-Safe Iterator
A fail-safe iterator operates on a copy (or snapshot) of the collection’s data, meaning any structural modifications to the collection will not affect the iterator. It does not throw ConcurrentModificationException
if the collection is modified while being iterated, but changes won’t reflect in the iteration process.
- Characteristics:
- Iterates over a clone or snapshot of the collection.
- Does not throw
ConcurrentModificationException
if the collection is modified during iteration. - Allows safe concurrent access but comes with extra memory overhead.
- Example collections:
CopyOnWriteArrayList
,ConcurrentHashMap
.
Example of Fail-Safe Iterator
import java.util.Iterator;
import java.util.concurrent.CopyOnWriteArrayList;
public class FailSafeExample {
public static void main(String[] args) {
CopyOnWriteArrayList list = new CopyOnWriteArrayList<>(new Integer[]{1, 2, 3, 4});
Iterator iterator = list.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
list.add(5); // Modifying the collection while iterating
}
System.out.println("Final List: " + list);
}
}
Output:
1
2
3
4
Final List: [1, 2, 3, 4, 5, 5, 5, 5]
In this example:
- The iterator works on a snapshot of the collection. Any modification made during iteration (like adding
5
in this case) is not reflected in the current iteration process. - The modification (addition of
5
) is made on the original collection, but the iterator safely completes the iteration over the initial snapshot.
Key Differences Between Fail-Fast and Fail-Safe
Feature | Fail-Fast | Fail-Safe |
---|---|---|
Behavior | Throws ConcurrentModificationException on modification | Continues iteration without exception |
Working Mechanism | Iterates directly over the collection | Iterates over a clone/snapshot of the collection |
Concurrent Access | Not suitable for concurrent access | Suitable for concurrent access |
Memory Overhead | No extra memory usage | Additional memory for snapshot/clone |
Examples | ArrayList , HashMap , HashSet | CopyOnWriteArrayList , ConcurrentHashMap |
When to Use Fail-Fast and Fail-Safe
- Fail-Fast: Use in single-threaded environments where detecting unintended concurrent modifications is crucial. It helps avoid unpredictable results when iterating through collections.
- Fail-Safe: Use in concurrent applications where collections may be modified by multiple threads. They ensure safe, concurrent access without exceptions but may incur additional memory overhead.