← Back to list

ConcurrentModificationException in Java

“Hello everyone, today we’re diving into a fundamental yet fascinating challenge that often arises when working with Java Collection…

Praveen Tripathi · 2024-08-11 16:49 · 107 claps · 3.9 min read paywalled
#java #collections-framework #collections-in-java #concurrentmodification #interview-preparation
Open on Medium ↗

ConcurrentModificationException in Java

“Hello everyone, today we’re diving into a fundamental yet fascinating challenge that often arises when working with Java Collection classes: the java.util.ConcurrentModificationException."

https://appmaster.io/api/_files/hRaLG2N4DVjRZJQzCpN2zJ/download/

https://appmaster.io/api/_files/hRaLG2N4DVjRZJQzCpN2zJ/download/

So, let's first discuss what a ConcurrentModificationException is.

A ConcurrentModificationException in Java occurs when a thread attempts to modify a collection while another thread is iterating over it. This is common when dealing with collections like ArrayList, HashMap, etc., that are not thread-safe.

Common Causes

  • Single-threaded Context: Modifying a collection (e.g., adding, removing elements) while iterating over it using an **Iterator** or enhanced for loop.


// 1. using enahnced for loop on ArrayList

        ArrayList<Integer> list = new ArrayList<>();
        list.add(11);
        list.add(12);
        list.add(13);
        list.add(17);

        for (Integer item: list){
            if (item == 13 || item.equals(12)){
                list.remove(item); // throw ConcurrentModificationException
            }
        }

        for (Integer item: list){
            if (item == 11){
                list.add(12); // throw ConcurrentModificationException
            }
        }

// 2. using iterator on ArrayList

        List<String> list1 = new ArrayList<>();
        list1.add("Alice");
        list1.add("Bob");
        list1.add("Charlie");

        Iterator<String> iterator = list1.iterator();

        while (iterator.hasNext()) {
            // throw ConcurrentModificationException
            String name = iterator.next();
            if (name.equals("Bob")) {
                list1.remove(name);  // Direct modification of the collection
            } else if (name.equals("Alice")) {
                list1.add("Harry");
            }
        }

// 3. iterating on HashMap

        HashMap<Integer, String> map = new HashMap<>();
        map.put(1, "Alice");
        map.put(2, "Bob");
        map.put(3, "Charlie");

        for (Map.Entry<Integer, String> entry : map.entrySet()) {
            if (entry.getValue().equals("Bob")) {
              map.remove(entry.getKey());  // ConcurrentModificationException
            }
        }
  • Multi-threaded Context: One thread is iterating over a collection while another thread modifies it concurrently.
        List<String> tList = new ArrayList<>(Arrays.asList("One", "Two"));

        Thread t1 = new Thread(() -> {
            for (String s : tList) {
                System.out.println(s);
            }
        });

        Thread t2 = new Thread(() -> {
            tList.add("new item");
        });

        t1.start();
        t2.start();
// Above code will throw ConcurrentModificationException

Ways to avoid this

  • Using iterator’s **removemethod: Instead of modifying the collection directly during iteration, use the iterator'sremove`** method.
List<String> list = new ArrayList<>();
list.add("Alice");
list.add("Bob");
list.add("Charlie");
list.add("two");

Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
    String s = iterator.next();
    if (s.equals("two")) {
        iterator.remove(); // Safe way to remove an element
    }
}

/*
  Since, iterator do not have add method.So, if we want add functionality
  as well, we can use ListIterator to acheive it.
*/

ListIterator<String> iterator = list.listIterator();

while (iterator.hasNext()) {
    String name = iterator.next();
    if (name.equals("Charlie")) {
        iterator.add("Tango");
    }
    else if(name.equals("Bob")){
        iterator.remove();
    }
}

System.out.println(Arrays.toString(list.toArray()));
// Output: [Alice, Charlie, Tango]
  • Using CopyOnWriteArrayList or ConcurrentHashMap: These collections are designed for safe concurrent modification.
  • **CopyOnWriteArrayList is a thread-safe variant of `ArrayList`**. It creates a copy of the underlying array on every write operation (add, remove, etc.). This means that any modifications made during iteration do not affect the original list being iterated over.
        CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();
        list.add("Alice");
        list.add("Bob");
        list.add("Charlie");

        for (String name : list) {
            if (name.equals("Bob")) {
                list.remove(name);  // Safe removal
            } else if (name.equals("Charlie")) {
                list.add("Tango"); // Safe addition
            }
        }

        System.out.println(list.size());  // Output: 3
  • **ConcurrentHashMap is a thread-safe implementation of `HashMap** that allows concurrent read and write operations. It does not throw aConcurrentModificationException` when the map is modified during iteration.
        ConcurrentHashMap<Integer, String> map = new ConcurrentHashMap<>();
        map.put(1, "Alice");
        map.put(2, "Bob");
        map.put(3, "Charlie");

        for (Map.Entry<Integer, String> entry : map.entrySet()) {
            if (entry.getValue().equals("Bob")) {
                map.remove(entry.getKey());  // Safe removal
            } else if (entry.getValue().equals("Charlie")) {
                map.put(4, "Tango"); // Safe addition
            }
        }

        System.out.println(map.size());  // Output: 3
  • Synchronizing the Collections: If you’re working with multiple threads, you can synchronize the collection manually.
List<String> list = Collections.synchronizedList(new ArrayList<>());
synchronized (list) {
    Iterator<String> iterator = list.iterator();
    while (iterator.hasNext()) {
        String s = iterator.next();
        if (s.equals("two")) {
            iterator.remove();
        }
    }
}
  • Using Streams: Java 8 streams provide a functional approach to avoid **ConcurrentModificationException**
        HashMap<Integer, String> map = new HashMap<>();
        map.put(1, "Alice");
        map.put(2, "Bob");
        map.put(3, "Charlie");

        map.entrySet().removeIf(item -> item.getValue().equals("Bob"));
        System.out.println(map);

        /* Output  
            {1=Alice, 3=Charlie}  
        */

        List<String> list1 = new ArrayList<>();
        list.add("Alice");
        list.add("Bob");
        list.add("Charlie");

        list.removeIf(item -> item.equals("Bob"));
        System.out.println(Arrays.toString(list.toArray()));

        /* Output
           [Alice, Charlie]
        */

Each of these approaches is suited to different scenarios, so the choice depends on your specific use case.

After looking at all above approach, there might one doubt can’t we use traditional for loop to perform this operation.

Answer to this question is that while it is true that we can use traditional for loop to iterate through list and also we can use it to remove elements while iterating without any exception, but while removing the removal shifts of elements can cause unexpected behavior.

Let’s discuss this with an example:

public record Employee(int id, String name, String gender, double salary, int age) {
}        

        ArrayList<Employee> employees = new ArrayList<>();
        employees.add(new Employee(1, "Alice", "", 200000.0, 23));
        employees.add(new Employee(1, "Bob", "", 200000.0, 23));
        employees.add(new Employee(1, "Tommy", "", 200000.0, 23));
        employees.add(new Employee(1, "John", "", 200000.0, 23));

        employees.removeIf(employee -> employee.name().equals("Bob"));
        System.out.println(employees.size());
        System.out.println(Arrays.toString(employees.stream().map(Employee::name).toArray())); 

        /* Output: 3
          ["Alice", "Tommy", "John"]
        */

        employees.add(new Employee(1, "Bob", "", 200000.0, 23));
        System.out.println(employees.size());
        System.out.println(Arrays.toString(employees.stream().map(Employee::name).toArray()));

        /* Output: 4
          ["Alice", "Tommy", "John", "Bob"]
        */

        for (int i = 0; i < employees.size(); i++) {
            if (employees.get(i).name().equals("Tommy") || employees.get(i).name().equals("Alice")) {
                employees.remove(i);
                /*
                    At first step it will remove Alice and then list becomes
                    ["Tommy", "John", "Bob"]
                    Now, since i incremented to 1, so it will never find Tommy
                    And, it will not be removed

                 */
            }
        }
        // size will come as 3
        System.out.println(employees.size());
        System.out.println(Arrays.toString(employees.stream().map(Employee::name).toArray()));
        /* Output: 3
          ["Tommy", "John", "Bob"]
        */

Key Points:

  • **removeIf:** Safe to use and does not cause issues since it internally manages the iteration.
  • Traditional for Loop: Modifying the list during iteration can lead to unexpected behavior, such as skipping elements. This happens because removing an element shifts all subsequent elements to the left, reducing the list size while the loop counter continues to increment.

Conclusion

Navigating ConcurrentModificationException is essential for writing reliable Java code. Understanding how different iteration methods—like using iterators, removeIf, or loop adjustments—affect collection modification helps you avoid common errors. By mastering these techniques, you can write cleaner, safer, and more efficient code. Keep exploring these concepts to strengthen your Java expertise. Hope you have a great learning experience.


메타데이터
post_id
cc477cb741df
slug
concurrentmodificationexception-in-java-cc477cb741df
url
https://medium.com/@praveentripathi236/concurrentmodificationexception-in-java-cc477cb741df
canonical_url
https://medium.com/@praveentripathi236/concurrentmodificationexception-in-java-cc477cb741df
author_url
https://medium.com/@praveentripathi236
status
ok
fetched_at
2026-08-01 14:51:42