ConcurrentModificationException
Fail Fast Iterators
- Iterators returned by ArrayList are called Fail Fast Iterators.
- Fail Fast Iterator is an iterator that attempts to raise an error if the sequence of elements processed by the iterator is changed during iteration.
- Because, if list is structurally 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 throw a ConcurrentModificationException. Thus, in the face of concurrent modification, the iterator fails quickly and cleanly, rather than risking arbitrary, non-deterministic behavior at an undetermined time in the future.
ConcurrentModificationException
The ConcurrentModificationException is used to fail-fast when something being iterated on is modified. This exception occurs when an object is attempted to be modified concurrently without permission. For example, if a Collection is modified while a thread is traversing it using an Iterator, a ConcurrentModificationException is thrown from the Iterator.next() method. The ConcurrentModificationException can occur in both multithreaded and single-threaded environments.
- Multithreaded environment - If a thread is traversing over a Collection using an Iterator and another thread attempts to add or remove elements to the Collection.
- Single-threaded environment - When an element is attempted to be removed from an ArrayList using the remove() method while it is being traversed using an enhanced for loop.
import java.util.ArrayList;
import java.util.List;
public class ConcurrentModificationExceptionExample {
public static void main(String args[]) {
List<String> list = new ArrayList<>();
list.add("a");
list.add("b");
list.add("c");
list.add("d");
for (String elem : list) {
if (elem.equals("a")) {
list.remove(elem);
}
}
}
}
Since the enhanced for loop uses an Iterator internally to traverse elements in a Collection, running the above code causes a ConcurrentModificationException since the remove() method of the Collection is used instead of the iterator:
How to Resolve ConcurrentModificationException
The above exception can be resolved by traversing the elements of the ArrayList using a traditional for loop instead of the enhanced for loop. Since the traditional for loop does not use an Iterator to traverse the elements of a Collection, it does not cause a ConcurrentModificationException:
Even though this is possible, it is not a good idea to remove elements of a list while running a for loop on it. Why? When we remove an entry from the list, list.size() is decremented by 1. This will result in unexpected errors at runtime. So, what is the preferred way of removing items from list? Use iterator.remove() as much as possible. That is the safest “and probably only safe” way of removing an object from a collection while iterating it. First retrieve the Iterator, perform a loop and remove when needed.
import java.util.ArrayList;
import java.util.List;
public class ConcurrentModificationExceptionExample {
public static void main(String args[]) {
List<String> list = new ArrayList<>();
list.add("a");
list.add("b");
list.add("c");
list.add("d");
for (int i = 0; i < list.size(); i++) {
if (list.get(i).equals("a")) {
list.remove(list.get(i));
}
}
System.out.println(list);
}
}
Since the ConcurrentModificationException belongs to the Iterator and not the remove() method of the ArrayList, running the above code will produce the correct output as expected.
The above exception can also be resolved by using an Iterator to traverse the elements of the ArrayList and using the Iterator.remove() method to remove elements. Alternatively, the Collection.removeIf() method introduced in Java 8 can be used to remove an element from a Collection if a given condition is true.
How to Avoid ConcurrentModificationException in Multithreaded Environments
To avoid the ConcurrentModificationException in multithreaded environments, certain precautions can be used:
- Iterating over an array instead of a collection - this can work well with small-sized lists but can degrade performance for larger ones.
- Locking the collection by placing it in a synchronized block - this may not be the most effective approach as it does not utilize the very purpose of multi-threading.
- Using Java concurrent collections such as ConcurrentHashMap and CopyOnWriteArrayList classes can help avoid the ConcurrentModificationException.