FailFast vs FailSafe
When iterating over collections in Java, it is important to understand how iterators behave when the underlying collection is modified during iteration. Modifying a collection while iterating over it can lead to inconsistent states and unpredictable behavior.
To handle this scenario, Java provides two types of iterator behaviors:
- Fail-Fast Iterators
- Fail-Safe Iterators
These mechanisms ensure that collection iteration remains safe and predictable, especially in concurrent or multi-threaded environments.
The Concurrent Modification Problem
A common problem occurs when a collection is modified while it is being iterated.
Example:
List<String> list = new ArrayList<>(Arrays.asList("A", "B", "C", "D"));
for (String item : list) {
if (item.equals("B")) {
list.remove(item); // modifying during iteration
}
}This results in:
ConcurrentModificationExceptionThis happens because:
- The iterator maintains an internal position.
- Structural changes modify the collection’s state.
- The iterator can no longer reliably continue iteration.
To detect or prevent such problems, Java collections implement fail-fast or fail-safe iteration strategies.
Fail-Fast Iterators
A fail-fast iterator immediately throws a ConcurrentModificationException if the collection is structurally modified after the iterator is created, except when the modification occurs through the iterator itself.
Fail-fast iterators operate directly on the original collection.
Structural Modification
Structural modifications are operations that change the size or structure of the collection.
Examples:
add()remove()clear()
Non-structural operations such as set() or element access do not trigger fail-fast behavior.
Internal Working Mechanism
Fail-fast iterators use an internal modification counter called modCount.
Simplified concept:
class ArrayList<E> {
int modCount = 0;
public boolean add(E e){
modCount++;
}
private class Itr implements Iterator<E>{
int expectedModCount = modCount;
public E next(){
if(modCount != expectedModCount){
throw new ConcurrentModificationException();
}
}
}
}Working Process
- When an iterator is created, it stores the current
modCount. - Each iteration compares the stored value with the current
modCount. - If the values differ, a
ConcurrentModificationExceptionis thrown.
This mechanism ensures early detection of incorrect modifications.
Collections with Fail-Fast Iterators
Most collections in the java.util package use fail-fast iterators.
Examples include:
ArrayListLinkedListHashMapHashSetTreeMapTreeSetLinkedHashMapLinkedHashSetPriorityQueue
Example
List<String> list = new ArrayList<>(Arrays.asList("A","B","C"));
Iterator<String> it = list.iterator();
list.add("D"); // modification after iterator creation
it.next(); // throws ConcurrentModificationExceptionSafe Removal Using Iterator
Fail-fast collections allow modification through the iterator itself.
Iterator<String> it = list.iterator();
while(it.hasNext()){
String item = it.next();
if(item.equals("B")){
it.remove(); // safe removal
}
}This works because the iterator updates its internal modification state.
Fail-Safe Iterators
A fail-safe iterator does not throw ConcurrentModificationException when the collection is modified during iteration.
Instead, it operates on a separate copy or snapshot of the collection.
Because the iterator reads from the snapshot, modifications to the original collection do not affect the iteration process.
Key Characteristics
Fail-safe iterators:
- Work on a copy or snapshot
- Do not throw exceptions
- Allow concurrent modification
- May show stale or outdated data
Collections with Fail-Safe Iterators
Fail-safe behavior is mainly found in collections from the java.util.concurrent package.
Examples include:
CopyOnWriteArrayListCopyOnWriteArraySetConcurrentHashMapConcurrentSkipListMapConcurrentLinkedQueue
Example with CopyOnWriteArrayList
List<String> list = new CopyOnWriteArrayList<>(Arrays.asList("A","B","C"));
Iterator<String> it = list.iterator();
list.add("D"); // modification allowed
while(it.hasNext()){
System.out.println(it.next());
}Output:
A
B
CThe iterator does not see "D" because it iterates over the snapshot taken at creation time.
Weakly Consistent Iterators
Some concurrent collections use weakly consistent iterators.
Examples include:
ConcurrentHashMap
These iterators:
- Do not throw exceptions
- May reflect some concurrent modifications
- Do not guarantee to reflect all updates
Example:
Map<String,Integer> map = new ConcurrentHashMap<>();
map.put("A",1);
map.put("B",2);
for(String key : map.keySet()){
map.put("C",3); // allowed
}The iteration continues safely.

Handling Concurrent Modifications
Several approaches can be used to safely modify collections during iteration.
1. Using Iterator Remove
Iterator<Integer> it = list.iterator();
while(it.hasNext()){
Integer num = it.next();
if(num % 2 == 0){
it.remove();
}
}2. Using removeIf (Java 8)
list.removeIf(num -> num % 2 == 0);This is the simplest modern approach.
3. Collect and Modify Later
List<String> toRemove = new ArrayList<>();
for(String item : list){
if(condition){
toRemove.add(item);
}
}
list.removeAll(toRemove);4. Using Concurrent Collections
List<String> list = new CopyOnWriteArrayList<>();
for(String item : list){
list.remove(item); // safe
}
Summary
-
Fail-fast and fail-safe iterators represent two different strategies for handling concurrent modifications in Java collections.
-
Fail-fast iterators operate directly on the original collection and detect structural modifications using a modification counter, throwing a
ConcurrentModificationExceptionwhen inconsistencies occur. -
Fail-safe iterators operate on a copy or snapshot of the collection, allowing concurrent modifications without throwing exceptions, but potentially returning stale data.
-
Understanding the differences between these two mechanisms helps developers choose the appropriate collection type and ensures safe, efficient iteration in both single-threaded and multi-threaded applications.
Written By: Muskan Garg
How is this guide?
Last updated on
