☕ Java Q63 / 162

What is ConcurrentModificationException?

AI-Powered Answer ✓ Answered

The `ConcurrentModificationException` (CME) is a runtime exception in Java's Collections Framework that typically occurs when an object is concurrently modified while it is being iterated over. This exception is part of the 'fail-fast' behavior of many Java collection classes.

What is ConcurrentModificationException?

A ConcurrentModificationException is thrown to indicate that a collection has been concurrently modified in an unexpected way. This usually means that one thread (or even the same thread) is iterating over a collection, and another thread (or the same thread directly) modifies the collection's structure (e.g., adding, removing, or updating elements in a way that changes its size or internal structure) during that iteration. The iterators returned by the iterator() method of most java.util collection implementations are fail-fast: if the collection is modified at any time after the iterator is created in any way except through the iterator's own remove() or add() methods, the iterator will immediately throw a ConcurrentModificationException.

Common Causes

  • Modifying a collection while iterating over it using an enhanced for-loop (for-each loop): This is the most common scenario, as the enhanced for-loop internally uses an iterator.
  • Modifying a collection while iterating over it using a traditional Iterator directly, but not using the iterator's remove() method for removal.
  • Multiple threads modifying a shared collection concurrently without proper synchronization. Even if the modification itself is thread-safe, the iterator might still throw CME if it's not designed for concurrent modifications.

Example of ConcurrentModificationException

Consider the following Java code snippet where we try to remove an element from an ArrayList while iterating over it using a for-each loop:

java
import java.util.ArrayList;
import java.util.List;

public class CMEExample {
    public static void main(String[] args) {
        List<String> names = new ArrayList<>();
        names.add("Alice");
        names.add("Bob");
        names.add("Charlie");
        names.add("David");

        System.out.println("Original list: " + names);

        try {
            for (String name : names) {
                System.out.println("Processing: " + name);
                if (name.equals("Bob")) {
                    names.remove(name); // This will throw ConcurrentModificationException
                }
            }
        } catch (ConcurrentModificationException e) {
            System.err.println("Caught ConcurrentModificationException: " + e.getMessage());
        }

        System.out.println("List after attempted modification: " + names);
    }
}

The output will show the exception being caught, because names.remove(name) modifies the names list directly while the for-each loop (which uses an implicit iterator) is active.

Solutions and Best Practices

  • Use Iterator.remove(): If you need to remove elements during iteration, use the remove() method provided by the Iterator interface. This is the only safe way to modify a collection during iteration for most standard collections.
  • Iterate over a copy: Create a new list or array with the elements you want to iterate over and then modify the original list. This is memory-intensive for large collections.
  • Use concurrent collections: For multi-threaded environments, use classes from the java.util.concurrent package like CopyOnWriteArrayList or ConcurrentHashMap. These collections are designed for concurrent access and typically use 'fail-safe' iterators, meaning they won't throw CME because they operate on a snapshot of the collection.
  • Synchronize access: If using standard collections in a multi-threaded context, ensure all access (iteration and modification) is properly synchronized using synchronized blocks or java.util.concurrent.locks.Lock mechanisms.
  • Avoid structural modifications: If possible, collect items to be removed/added in a separate list and then perform the modifications after the iteration is complete.

Example using `Iterator.remove()`

java
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

public class CMESafeExample {
    public static void main(String[] args) {
        List<String> names = new ArrayList<>();
        names.add("Alice");
        names.add("Bob");
        names.add("Charlie");
        names.add("David");

        System.out.println("Original list: " + names);

        Iterator<String> iterator = names.iterator();
        while (iterator.hasNext()) {
            String name = iterator.next();
            System.out.println("Processing: " + name);
            if (name.equals("Bob")) {
                iterator.remove(); // This is safe and will not throw CME
            }
        }

        System.out.println("List after safe modification: " + names);
    }
}