How to Prevent ConcurrentModificationException When Modifying a List While Iterating Over It in Java etd_admin, November 23, 2024November 23, 2024 The ConcurrentModificationException is a common error that occurs when you modify a List while iterating over it using methods like for-each or Iterator. This article will explain why it happens and explore different ways to avoid it with simple explanations and code examples. Why Does ConcurrentModificationException Occur? In Java, most List implementations, such as ArrayList, are fail-fast. When you iterate over a list using an Iterator, the underlying structure of the list is monitored for changes. If the structure changes (e.g., an element is added or removed) during iteration, the Iterator detects this and throws a ConcurrentModificationException to prevent unpredictable behavior. import java.util.ArrayList; import java.util.List; public class Example { public static void main(String[] args) { List<String> list = new ArrayList<>(); list.add("A"); list.add("B"); list.add("C"); for (String item : list) { if (item.equals("B")) { list.remove(item); // This will throw ConcurrentModificationException } } } } Solutions to Prevent ConcurrentModificationException Here are several approaches to safely modify a List while iterating over it: 1. Use Iterator.remove() Instead of directly modifying the list, use the remove() method provided by the Iterator. This is the only safe way to remove elements while iterating. import java.util.ArrayList; import java.util.Iterator; import java.util.List; public class Example { public static void main(String[] args) { List<String> list = new ArrayList<>(); list.add("A"); list.add("B"); list.add("C"); Iterator<String> iterator = list.iterator(); while (iterator.hasNext()) { String item = iterator.next(); if (item.equals("B")) { iterator.remove(); // Safe removal } } System.out.println(list); // Output: [A, C] } } 2. Use a ListIterator for More Control If you need more control, such as replacing elements during iteration, use a ListIterator. It supports both removal and addition. import java.util.ArrayList; import java.util.List; import java.util.ListIterator; public class Example { public static void main(String[] args) { List<String> list = new ArrayList<>(); list.add("A"); list.add("B"); list.add("C"); ListIterator<String> listIterator = list.listIterator(); while (listIterator.hasNext()) { String item = listIterator.next(); if (item.equals("B")) { listIterator.remove(); // Safe removal listIterator.add("D"); // Add a new element } } System.out.println(list); // Output: [A, D, C] } } 3. Use a Copy of the List If modifying the original list directly isn’t a requirement, you can iterate over a copy of the list while modifying the original. import java.util.ArrayList; import java.util.List; public class Example { public static void main(String[] args) { List<String> list = new ArrayList<>(); list.add("A"); list.add("B"); list.add("C"); for (String item : new ArrayList<>(list)) { if (item.equals("B")) { list.remove(item); // Safe because we are iterating over a copy } } System.out.println(list); // Output: [A, C] } } 4. Use Java Streams In Java 8 and later, streams provide a concise way to filter and collect elements into a new list. import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; public class Example { public static void main(String[] args) { List<String> list = new ArrayList<>(); list.add("A"); list.add("B"); list.add("C"); list = list.stream() .filter(item -> !item.equals("B")) // Remove "B" .collect(Collectors.toList()); System.out.println(list); // Output: [A, C] } } 5. Use a Concurrent Collection For multithreaded environments, consider using a concurrent collection like CopyOnWriteArrayList. This collection creates a copy of the list during modifications, ensuring thread-safety. import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; public class Example { public static void main(String[] args) { List<String> list = new CopyOnWriteArrayList<>(); list.add("A"); list.add("B"); list.add("C"); for (String item : list) { if (item.equals("B")) { list.remove(item); // Safe in CopyOnWriteArrayList } } System.out.println(list); // Output: [A, C] } } When to Use Each Approach? Iterator.remove() or ListIterator: For single-threaded environments where you need precise control over modifications. Copy of the List: When modifications don’t need to reflect immediately in the current iteration. Streams: For concise, functional-style filtering and transformation. Concurrent Collections: In multi-threaded applications requiring thread-safe operations. The ConcurrentModificationException occurs because most List implementations are fail-fast, designed to detect structural modifications during iteration. By using techniques like Iterator.remove(), ListIterator, copying the list, streams, or concurrent collections, you can safely modify a list without encountering this exception. Java ExceptionsJavaLists