Thursday, August 4, 2022

How to Avoid/Fix ConcurrentModificationException while looping over ArrayList in Java [Example]

Apart from the NullPointerException and ClassNotFoundException, ConcurrentModificationException is another nightmare for Java developers. What makes this error tricky is the word concurrent, which always mislead Java programmers that this exception is coming because multiple threads are trying to modify the collection at the same time. Then begins the hunting and debugging, they spent countless hours to find the code which has the probability of concurrent modification. While in reality, ConcurrentModficationException can also come in a single-threaded environment. 

To give you an example, just loop over a list using for loop and try to remove one element, you will get the ConcurrentModificatoinExcetpion? Why? because you broke the rule of not modifying a Collection during iteration.

How does Java know to throw ConcurrentModificationExeption? It uses a transient variable called modCount, which keeps track of how many times a list is modified structurally. Structural modifications are those that change the size of the list, which may affect the progress of iteration and may yield incorrect results.

Both Iterator and ListIterator use this field to detect unexpected change. Other methods of List which structurally modify List also use this method like add(), remove().


Problem: loop over an ArrayList and remove selected elements, but remove() is throwing "Exception in thread "main" java.util.ConcurrentModificationException".



Cause: The real cause of ConcurrentModfiicationException is inconsistent modCount. When you are iterating over ArrayList then Iterator's next() method keep track of modCount. If you modify the collection by adding or removing elements then modCount will change and it will not match with the expected modCount, hence Iterator will throw ConcurrentModificationException.

Here is the code snippet from the hasNext() method which shows there is check for modCount:

public E next() {
   checkForComodification();
   int i = cursor;
   if (i >= size)
        throw new NoSuchElementException();
    Object[] elementData = ArrayList.this.elementData;
    if (i >= elementData.length)
        throw new ConcurrentModificationException();
    cursor = i + 1;
    return (E) elementData[lastRet = i];
 }

Now if you check this checkForComodification() method, you will find what I just said:

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


Solution: Use Iterator if you are doing it on the single-threaded environment, otherwise use concurrent collection classes like CopyOnWriteArrayList to remove elements while you are looping over it.




Solving ConcurrentModificationException while Iterating over  ArrayList in Java

Here is the Java program to demonstrate one scenario where you get the ConcurrentModificationException even if just one thread is modifying the ArrayList. In this example, we are looping over ArrayList using advanced for loop and removing selected elements, but because we are using ArrayList's remove() method.


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

/**
 * Java Program to demonstrate how to deal with
 * ConcurrentModificationException.
 * Unlike the name suggests, this error can come even if only 
 * one thread is modifying the collection e.g. List. 
 * It happens when you modify collection 
 * while iterating over it e.g. adding new element or removing elements.
 * 
 * If you want to remove elements while traversing list then 
 * make sure you use Iterator's remove() method or not ArrayList's remove()
 * method() to avoid ConcurrentModificationExcetpion.
 * 
 * @author WINDOWS 8
 *
 */
public class ConcurrentModExceptionDemo{

    public static void main(String args[]) {

        List<String> listOfPhones = new ArrayList<String>(Arrays.asList(
                "iPhone 6S", "iPhone 6", "iPhone 5", "Samsung Galaxy 4",
                 "Lumia Nokia"));
        
        System.out.println("list of phones: " + listOfPhones);
        
        // Iterating and removing objects from list
        // This is wrong way, will throw ConcurrentModificationException
        for(String phone : listOfPhones){
            if(phone.startsWith("iPhone")){
               // listOfPhones.remove(phone); // will throw exception
            }
        }
        
        // The Right way, iterating elements using Iterator's remove() method
        
        for(Iterator<String> itr = listOfPhones.iterator();
                                   itr.hasNext();){            
            String phone = itr.next();            
            if(phone.startsWith("iPhone")){
                // listOfPhones.remove(phone);  // wrong again
                itr.remove(); // right call
            }
        }
        
        System.out.println("list after removal: " + listOfPhones);

    }

}

Output :
list of phones: [iPhone 6S, iPhone 6, iPhone 5, Samsung Galaxy 4,
 Lumia Nokia]
list after removal: [Samsung Galaxy 4, Lumia Nokia]

If you uncomment the commented code in the first loop and second loop, you will 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 dto.ReverseArrayInPlace.main(ReverseArrayInPlace.java:28)

because we are using ArrayList's remove() method. In the second example, we have used the remove() method of Iterator and that's why we are successfully able to delete selected elements from the ArrayList without ConcurrentModificationException.


How to Fix ConcurrentModficiationException in Java?

Here is a summary of important points about solving ConcurrentModificationException while looping or Iterating over ArrayList in Java :

How to solve ConcurrentModificationException in Java


That's all about how to deal with ConcurrentModificationException in Java. The biggest thing to learn and remember is that this error can come even if you have just one thread modifying collection like removing elements while looping over the list

So, while the name suggest concurrentModification it doesn't always mean that multiple threads are modifying the Collection or ArrayList at the same time. The simplest scenarios when this error comes when you are iterating over an ArrayList in Java and then using ArrayList.remove() method to remove objects. 

Same is true when you sue the for each loop of Java 5 to loop over Collection or List as it internally uses Iterator. You can fix ConcurrentModificationException by changing your code and instead of using ArrayList.remove() method just use Iterator.remove() method in Java. 


Related troubleshooting guides
Here are some handy Java tips to solve some common errors and exceptions in Java:
  • How to deal with java.lang.NoClassDefFoundError: org/apache/xmlbeans/XmlObject? (solution)
  • How to solve "could not create the Java virtual machine" error in Java? (solution)
  • Fixing java.lang.unsupportedclassversionerror unsupported major.minor version 50.0 (solution)
  • java.lang.ClassNotFoundException: org.apache.commons.logging.LogFactory error (solution)
  • How to solve java.lang.ClassNotFoundException: com.mysql.jdbc.Driver error? (hint)
  • How to solve java.lang.classnotfoundexception oracle.jdbc.driver.oracledriver? (solution)
  • java.lang.ClassNotFoundException : org.Springframework.
    Web.Context.ContextLoaderListener (solution)
  • How to fix 'javac' is not recognized as an internal or external command (solution)
  • How to fix Caused By: java.lang.NoClassDefFoundError: org/apache/log4j/Logger (solution)
  • How to solve java.lang.OutOfMemoryError: Java Heap Space in Eclipse, Tomcat? (solution)

5 comments:

  1. Still i get concurrentModificationException


    // LocalDate today = LocalDate.now();
    SimpleDateFormat sd = new SimpleDateFormat("yyyy-MM-dd");
    String today = sd.format((DateUtil.getCurrentDate()));
    List orderDetail = new ArrayList();
    orderDetail = this.orderDetailDao.getAllOrders(hotel.getId());
    if (orderDetail != null){
    System.out.println("orderDetail....."+orderDetail);
    for(Iterator objOrderDetail = orderDetail.iterator();objOrderDetail.hasNext();){
    OrderDetailDTO objDetail = objOrderDetail.next();
    System.out.println("obj....."+objDetail);
    if(today.equals(sd.format(objDetail.getCreatedDate()))){
    System.out.println("dd=" + today);
    List toppings = new ArrayList();
    toppings = this.toppingDao.getToppingsByItemId(objDetail.getId());
    String top = String.join(",", toppings);
    System.out.println("top " + top);
    objDetail.setToppingName(top);
    orderDetail.add(objDetail);
    System.out.println("obj.............."+objDetail);
    //objOrderDetail.remove();
    }
    }
    }
    System.out.println("orderDetail List......"+orderDetail);
    return orderDetail;

    ReplyDelete
  2. Please let me know if any drawback in using below code

    List listOfPhones_Ashish = new ArrayList
    (Arrays.asList( "iPhone 6S", "iPhone 6", "iPhone 5", "Samsung Galaxy 4", "Lumia Nokia"));
    System.out.println("list of phones: " + listOfPhones_Ashish);
    for(int i=0;i<listOfPhones_Ashish.size();i++){
    if(listOfPhones_Ashish.get(i).startsWith("iPhone")){
    listOfPhones_Ashish.remove(i);
    --i;
    }
    }
    System.out.println("After my removal process list is "+listOfPhones_Ashish);

    ReplyDelete
    Replies
    1. no, works the same way as iterators. Only for-in construct results in this exception.

      Delete
  3. If you are using Java 8 and above, just use list.filter to filter out and return a list with the items that satisfies a condition.

    Also, wrap the list in a syncronised object so only one callaer can access and modify it

    ReplyDelete
    Replies
    1. I have never tried that approach but sounds promising, did you tried it on production yet?

      Delete

Feel free to comment, ask questions if you have any doubt.