fail-fast vs fail-safe Iterators in Java

1. Introduction

In Java, iterators are used to traverse collections. There are two types of iterators in terms of their behavior when the underlying collection is modified while iteration is in progress: fail-fast and fail-safe. 

Fail-fast iterators immediately throw a ConcurrentModificationException if they detect that the collection has been modified during iteration. On the other hand, Fail-safe iterators do not throw any exception because they operate on a copy of the collection.

2. Key Points

1. Fail-fast iterators throw ConcurrentModificationException on detecting modifications made to a collection during iteration.

2. Fail-safe iterators allow modifications to a collection while iterating because they work on a clone of the collection.

3. Fail-fast iterators are typically provided by collections in the java.util package, such as ArrayList and HashMap.

4. Fail-safe iterators are often used in concurrent collections from the java.util.concurrent package, like CopyOnWriteArrayList.

3. Differences

Fail-Fast Fail-Safe
Operate directly on the collection itself. Operate on a clone of the collection, not the original collection.
Will throw a ConcurrentModificationException if the collection is modified while iterating, other than through the iterator's own remove() method. It will not throw a ConcurrentModificationException if the collection is modified during iteration.
Examples include iterators from ArrayList, HashSet, and HashMap. Examples include iterators from classes like CopyOnWriteArrayList and ConcurrentHashMap.
They are faster as they do not involve cloning or additional overhead. They are slower and consume more memory as they work on a clone of the collection.
Suitable for single-threaded environments where modification of a collection during iteration is not expected. Suitable for multi-threaded environments where modifications to a collection during iteration are expected or possible.

4. Example

// Example of Fail-Fast Iterator
import java.util.ArrayList;
import java.util.Iterator;

public class FailFastExample {
    public static void main(String[] args) {
        ArrayList<Integer> numbers = new ArrayList<>();
        numbers.add(1);
        numbers.add(2);
        numbers.add(3);

        Iterator<Integer> iterator = numbers.iterator();

        while (iterator.hasNext()) {
            Integer number = iterator.next();
            System.out.println(number);
            if (number.equals(2)) {
                // Modifying ArrayList after creating Iterator should throw ConcurrentModificationException
                numbers.remove(number);
            }
        }
    }
}

// Example of Fail-Safe Iterator
import java.util.concurrent.CopyOnWriteArrayList;

public class FailSafeExample {
    public static void main(String[] args) {
        CopyOnWriteArrayList<Integer> numbers = new CopyOnWriteArrayList<>();
        numbers.add(1);
        numbers.add(2);
        numbers.add(3);

        Iterator<Integer> iterator = numbers.iterator();

        while (iterator.hasNext()) {
            Integer number = iterator.next();
            System.out.println(number);
            if (number.equals(2)) {
                // Modifying CopyOnWriteArrayList after creating Iterator doesn't throw any exception
                numbers.remove(number);
            }
        }
    }
}

Output:

// Output from FailFastExample:
1
2
Exception in thread "main" java.util.ConcurrentModificationException
// Output from FailSafeExample:
1
2
3

Explanation:

1. In the FailFastExample, the ArrayList is modified after the iterator is created, which triggers a ConcurrentModificationException.

2. In the FailSafeExample, the CopyOnWriteArrayList is modified during iteration, but since the iterator works on a separate copy, no exception is thrown and iteration continues for all elements.

5. When to use?

- Use fail-fast iterators when you want to prevent concurrent modification and ensure the collection remains unchanged during iteration.

- Use fail-safe iterators when you need to allow modifications to the collection while iterating, such as in a multithreaded environment where you don't want an iterator's behavior to be affected by other threads' actions.

Comments