ConcurrentModificationException - Issues & Resolve in Java - Java @ Desk

Friday, November 28, 2014

ConcurrentModificationException - Issues & Resolve in Java

ConcurrentModificationException - Issues & Resolve in Java What is ConcurrentModificationException?
A ConcurrentModificationException occurs when we try to remove an item from a list at the same time as we iterate through its elements.

A simple example for an erroneous code is as follows:

for (String str : strList) {
        if (isDeleteStr(str)) { //Condition
            strList.remove(str); //Removing using the list itself
        }
}


Avoiding ConcurrentModificationException – The complete list of do’s and don’ts

There are several methods by which we can overcome this exception:

1. Use CopyOnWriteArrayList
Click here for explanation on CopyOnWriteArrayList

2. Use an Iterator and call the remove() method
You can make use of an iterator instead of using the collection itself.
Sample code:

Iterator<String> iterator = strList.iterator();
while (iterator.hasNext()) {
 String str = iterator.next();
 if (isDeleteStr(str)) { //Condition
            iterator.remove(); //Removing using the iterator does not create exception
        }
}


3. Using Lambda function (Only applicable for Java 8)
strList.removeIf( e -> (condition) );


4. Copy the list to an array and then remove it
You can copy the entire list to an array, iterate through this array and remove the element from it. However, this method is not advisable for large collections, as it will create performance issues.

More reading – the How, the What, the Whys

Why does this error occur?
Java does not generally allow one thread to modify a collection while another thread is iterating over it, because the results of the iteration are undefined under these circumstances. Java provides fail-fast iterators to iterate through collections. Fail-Fast iterators fail quickly and cleanly when these types of modifications occur, rather than risking a non-deterministic behavior in the future that may occur due to the modification.

The ArrayList implementation uses the following field to provide a fail-fast iterator:
protected transient int modCount;


The modCount field indicates the number of times the collection has been structurally modified. In any case, if the value of the modCount field changes unexpectedly, a ConcurrentModificationException is thrown. The error is not actually thrown from the remove part. On the contrary, it is thrown when the iterator calls iterator.next(), as it is from here that we check whether the list has been modified or not.

Sample code:

public static void iteratorMethod(){
 List<String> nameList = getNames();
 Iterator<String> nameIterator = nameList.iterator();
 while(nameIterator.hasNext()){
  String name = nameIterator.next(); //Line 18..  
  if(name.equalsIgnoreCase("Caesar")
   nameList.remove(name); // Throws Exception
  }
  System.out.println(nameList);
 }
}
private static List<String> getNames(){
 List<String> nameList = new ArrayList<String>();
 nameList.add("Julius");
 nameList.add("Caesar");
 nameList.add("Mark");
 nameList.add("Antony");
 return nameList;
}


When executing iteratorMethod(), we get the following exception:

Exception in thread "main" java.util.ConcurrentModificationException
 at java.util.ArrayList$Itr.checkForComodification(Unknown Source)
 at java.util.ArrayList$Itr.next(Unknown Source)
 at ConcurrentSample.iteratorMethod(ConcurrentSample.java:18)
 at ConcurrentSample.main(ConcurrentSample.java:10)


Let us take a look at how things would have changed under different coding scenarios.
What happens when we change nameList.remove(name) to nameIterator.remove()?
If we make this change and execute the code, it works as expected, and the output received is: [Julius, Mark, Antony] //It was [Julius, Caesar, Antony] before.

What happens when we change nameList.remove(name) to nameList.add("New Name")?
If we make this change and execute the code, we again get the ConcurrentModificationException, because the modCount field in the nameList has been changed, as there is a structural modification to the list.

What happens when we change the condition from name.equalsIgnoreCase("Caesar") to name.equalsIgnoreCase("Mark") and use nameList.remove(name) itself?


If we make this change and execute the code, it works without exception!!, and the output received is: [Julius, Caesar, Antony]
As you can see, this does not create an exception even though we used the list itself to remove instead of the iterator. Why does this do not throw an exception?

It is because the ConcurrentModificationException is thrown from:

String name = nameIterator.next(); 


It is during this operation that we check for the structural modification of the list. However as "Mark" is the second last element in the list, removing that will cause the nameIterator.hasNext() condition to return false, and hence, after the modification, nameIterator.next() is not called.

This post is written by Jerin Joseph. He is a freelance writer, loves to explore latest features in Java technology.






1 comment: