Concurrent package (java.util.concurrent) contains thread-safe collection classes that allow collections to be modified while iterating. By design Iterator implementation in java.util packages are fail-fast and throws ConcurrentModificationException. But Iterator implementation in java.util.concurrent packages are fail-safe and we can modify the collection while iterating. Some of these classes are CopyOnWriteArrayList, ConcurrentHashMap, CopyOnWriteArraySet.
package com.journaldev.collections;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
public class ConcurrentListExample {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("1");
list.add("2");
list.add("3");
list.add("4");
list.add("5");
// get the iterator
Iterator<String> it = list.iterator();
//manipulate list while iterating
while(it.hasNext()){
System.out.println("list is:"+list);
String str = it.next();
System.out.println(str);
if(str.equals("2"))list.remove("5");
if(str.equals("3"))list.add("3 found");
//below code don't throw ConcurrentModificationException
//because it doesn't change modCount variable of list
if(str.equals("4")) list.set(1, "4");
}
}
}
When we run above program, we get java.util.ConcurrentModificationException as soon as the ArrayList is modified. It happens because ArrayList iterator is fail-fast by design. What it means is that once the iterator is created, if the ArrayList is modified, it throws ConcurrentModificationException so basically iterator throws ConcurrentModificationException if list size is changed.
if we want to add or remove elements from the list because we find some specific element, in that case we should use Concurrent Collection class – CopyOnWriteArrayList. This is a thread-safe variant of java.util.ArrayList in which all mutative operations (add, set, and so on) are implemented by making a fresh copy of the underlying array.
CopyOnWriteArrayList introduces extra overload to the processing but it’s very effective when number of modifications are minimal compared to number of traversal operations.
If we change the implementation to CopyOnWriteArrayList, then we don’t get any exception and below is the output produced.
list is:[1, 2, 3, 4, 5]
1
list is:[1, 2, 3, 4, 5]
2
list is:[1, 2, 3, 4]
3
list is:[1, 2, 3, 4, 3 found]
4
list is:[1, 4, 3, 4, 3 found]
5
and it allows the modification of list, but it doesn’t change the iterator and we get same elements as it was on original list.