Saturday, July 17, 2021

How to avoid of fix ConcurrentModificationException in Java? Example

One of the common problems while removing elements from an ArrayList in Java is the ConcurrentModificationException. If you use classical for loop with the index or enhanced for loop and try to remove an element from the ArrayList using remove() method, you will get the ConcurrentModificationException but if you use Iterator's remove method or ListIterator's remove() method, then you won't get this error and be able to remove the element. It's an unwritten rule in Java that while looping through the list, you should not add() or remove() elements until the collection supports fail-safe Iterator e.g. CopyOnWriteArrayList, which operate on a copy of list rather than the original list.

The main problem with this error is that it confuses developers that the list is getting modified by multiple threads and that's why Java is throwing this error, it's not true. Most of the time ConcurrentModificationException comes even without multiple threads modifying the list.

It's misnomer, don't get fooled away by this. though it seems natural thinking that maybe some other thread is trying to modify the collection at the same time, it's usually breaking the Java rule.

In this article, I'll explain this error and we'll many code examples to reproduce this code even with a single thread and learn how we can avoid concurrent modification errors while modifying an ArrayList in Java.

Btw, if you are not familiar with collection classes e.g. ArrayList itself then you can also join these Java Collections and Stream API Courses to learn this important API in depth. 



ConcurrentModificationException in Single Thread

This is the first example of reproducing the concurrent modification exception in Java. In this program, we are iterating over ArrayList using the enhanced foreach loop and removing selective elements e.g. an element that matches certain conditions using ArrayList's remove method.

For example, in the below code we have first added a couple of good programming books like  Programming Pearls, Clean Code, Effective Java, and Code Complete into ArrayList and then removing any element which has Code in its title.

package beginner;

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

public class HelloWorldApp{

   public static void main(String... args){
       List<String> listOfBooks = new ArrayList<>();  
       listOfBooks.add("Programming Pearls");
       listOfBooks.add("Clean Code");
       listOfBooks.add("Effective Java");
       listOfBooks.add("Code Complete");
       
       // Using forEach loop to iterate and removing 
       // element during iteration will throw 
       // ConcurrentModificationException in Java
       for(String book: listOfBooks){
           if(book.contains("Code"));{
               listOfBooks.remove(book);
           }
       }
   }

}
Output
Exception in thread "main" java.util.ConcurrentModificationException
    at java.util.ArrayList$Itr.checkForComodification(Unknown Source)
    at java.util.ArrayList$Itr.next(Unknown Source)
    at beginner.HelloWorldApp.main(HelloWorldApp.java:18)


You can see that this error comes even though we just have one thread, the main thread which is operating with ArrayList. The ConcurrentModification error comes because we are not using Iterator, instead just calling listOfBooks.remove() method.

In this code, I have used Java 1.5 enhanced for loop, you must know how enhanced for loop works in Java.

Difference between for loop and enhanced for loop is that later internally uses an Iterator for going over all elements of a collection. For a more in-depth discussion, see here



Using Classical for loop and ArrayList.remove(index)

Here is another interesting code example of removing elements from ArrayList. Surprisingly this code will not throw ConcurrentModificationException when you first run it? do you know why?

Well, try it before you look at the explanation after the code. It's really this kind of minor details about Java programming language and Collection framework, which will make you a good developer, and also help you to get your Java certification if you are preparing for it.

package beginner;

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

public class HelloWorldApp{

   public static void main(String... args){
       List<String> listOfBooks = new ArrayList<>();  
       listOfBooks.add("Programming Pearls");
       listOfBooks.add("Clean Code");
       listOfBooks.add("Effective Java");
       listOfBooks.add("Code Complete");
       
       System.out.println("List before : " + listOfBooks);
       for(int i=0; i<listOfBooks.size(); i++){
           String book = listOfBooks.get(i);
           if(book.contains("Programming")){
               System.out.println("Removing " + book);
               listOfBooks.remove(i); // will throw CME
           }
       }
       System.out.println("List after : " + listOfBooks);
   }

}

Output
List before : [Programming Pearls, Clean Code, Effective Java, Code Complete]
Removing Programming Pearls
List after : [Clean Code, Effective Java, Code Complete]

This code doesn't throw ConcurrentModificationException because here we are not using Iterator but we are just using traditional for loop.

It's the Iterator that throws ConcurrentModificationException, and not the remove method of ArrayList, hence you don't see that error in the below code.

If you look at the code for ArrayList.java, you will notice that there is a nested class that implemented Iterator interface and its next() method calls the checkForComodification() function which actually checks if ArrayList has modified during iteration or not, if modCount doesn't match with expectedModCount then it throws ConcurrentModificationException.

final void checkForComodification() {
  if (modCount != expectedModCount)
    throw new ConcurrentModificationException();
}

These kinds of questions are also very popular on Oracle Java Certification e.g. OCAJP (1z0-808) and OCPJP (1Z0-809), so if you are preparing for those exams, you should know the answer.

Here is the full code snippet from the ArrayList.java class for your quick reference:

How to deal with ConcurrentModificationException in Java?


Using Iterator but ArrayList's remove method

Now, let's see another code example, where the Java programmer thinks he has done everything right but still getting the concurrent modification exception. Can you spot the error? It's really common and I have seen this kind of code a lot of time on Java forums, StackOverflow, and on Facebook Java groups where they asked to fix the problem.

package beginner;

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

public class HelloWorldApp{

   public static void main(String... args){
       List<String> listOfBooks = new ArrayList<>();  
       listOfBooks.add("Programming Pearls");
       listOfBooks.add("Clean Code");
       listOfBooks.add("Effective Java");
       listOfBooks.add("Code Complete");
       
       Iterator<String> iterator = listOfBooks.iterator();
       while(iterator.hasNext()){
           String book = iterator.next();
           listOfBooks.remove(book);
       }
   }

}

Output
Exception in thread "main" java.util.ConcurrentModificationException
    at java.util.ArrayList$Itr.checkForComodification(Unknown Source)
    at java.util.ArrayList$Itr.next(Unknown Source)
    at beginner.HelloWorldApp.main(HelloWorldApp.java:18)

The real problem with this code is that even though the code is using Iterator to go over ArrayList, it's not really using the Iterator.remove() method to remove the element. It is just using Iterator to get the next element but calling the ArrayList.remove() method to delete the element.

I know, it looks easy when you know the reason but in real-time, many times programmers take even hours to figure out what is wrong. So, just beware of that.

Btw, if you are learning Java then I suggest joining Complete Java Masterclass to learn Java better and avoid such common errors.

How to fix with ConcurrentModificationException in Java?


The right way to remove elements is by using Iterator's remove method

Finally, here is the right way to delete an element from ArrayList during iteration. In this example, we have used Iterator to both iterate as well as remove the element. The code is ok but it has a serious limitation, you can only use this code to remove the current element. You cannot remove an arbitrary element from ArrayList in Java.

package beginner;

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

public class HelloWorldApp{

   public static void main(String... args){
       List<String> listOfBooks = new ArrayList<>();  
       listOfBooks.add("Programming Pearls");
       listOfBooks.add("Clean Code");
       listOfBooks.add("Effective Java");
       listOfBooks.add("Code Complete");
       
       System.out.println("List before : " + listOfBooks);
       Iterator<String> iterator = listOfBooks.iterator();
       while(iterator.hasNext()){
           String book = iterator.next();
           System.out.println("Removing " + book);
           iterator.remove();
       }
       System.out.println("List after : " + listOfBooks);
   }

}
Output
List before : [Programming Pearls, Clean Code, Effective Java, Code Complete]
Removing Programming Pearls
Removing Clean Code
Removing Effective Java
Removing Code Complete
List after : []

The same behavior is applicable to ListIterator as well. I mean you can replace Iterator with ListIterator and the code will work fine. The ListIterator also allows you to navigate in both directions like, forward and backward.

That's all about how to avoid ConcurrentModificationException while removing elements from ArrayList during iteration. You can use the same technique to avoid ConcurrentModificationException while removing elements from any other collection classes which have fail-fast Iterator e.g. LinkedList.

Other Java troubleshooting Guides you may like
  • How to solve ArrayIndexOutOfBoundsException in Java? (guide)
  • How to solve NullPointerException in Java? (guide)
  • How to solve the "System cannot find the path specified" error? (solution)
  • How to solve NoClassDefFoundError while running a Java program from a command line? (solution)
  • How to solve "No JVM found, Please install 64-bit JDK" error in Android Studio? (solution)
  • How to deal with SQLException "No Suitable driver found " error in JDBC and MySQL? (guide)
  • How to solve NumberFormatException in Java? (guide)
  • How to solve Minecraft - java.lang.UnsatisfiedLinkError: lwjgl64.dll : Access Denied? (solution)
  • How to fix java.lang.ArrayIndexOutOfBoundsException: 1 in Java? (solution)
  • How to fix java.net.SocketException: Software caused connection abort: recv failed (fix)

Thanks for reading this tutorial, if you like this tutorial then please share it with your friends and colleagues.  If you have any questions or suggestions then please drop a comment. 

7 comments :

J. Ernesto Aneiros said...

Sorry Jevin but this is not using the Java 8, just call removeIf with a predicate like:

lb.removeIf(t -> "Clean Code".equals(t));

To all reading this out there: Java 8 is a complete new animal!


J. Ernesto Aneiros said...

In fact the remove method doesn't throw that exception anymore!

listOfBooks.remove("Programming Pearls");

J. Ernesto Aneiros said...

Of course if multiple threads access a list concurrently, and at least one of the threads modifies the list structurally, it must be synchronized externally. (A structural modification is any operation that adds or deletes one or more elements, or explicitly resizes the backing array; merely setting the value of an element is not a structural modification.)

This is typically accomplished by synchronizing on some object that naturally encapsulates the list. If no such object exists, the list should be "wrapped" using the Collections.synchronizedList method. This is best done at creation time, to prevent accidental unsynchronized access to the list:

List list = Collections.synchronizedList(new ArrayList(...));

This was taken from: https://docs.oracle.com/javase/8/docs/api/java/util/ArrayList.html

javin paul said...

Hello -j aneiros , thanks for removeIf(), it's seriously good edition and much cleaner code. Regarding remove() not throwing exception, did you tested it in the condition where you iterate or loop and then remove?

Najstariji zanatlija said...

If we write like this, there is an exception :

for (String book : listOfBooks) {
if (book.contains("Code")) {
listOfBooks.remove(book);
}
}

On the other hand, if we write like this, there is NO concurrent modification exception !

for (String book : listOfBooks) {
if (book.contains("Java")) {
listOfBooks.remove(book);
}
}

Weird...

J. Ernesto Aneiros said...

@Najstariji zanatlija

Uhmm it doesn't look right, check your code, the exception will be thrown in both cases. Just in case: the contains method is case sensitive, behind the scene it uses indexOf which matches char by char.

J. Ernesto Aneiros said...

@Javin: The remove inside an extended for loop fails unfortunately, even converting to a functional implementation like this:

lb.stream().filter(t -> t.contains("Java")).forEach(t -> lb.remove(t));

Post a Comment