Sobes.tech
Middle

How to avoid the ConcurrentModificationException when iterating over a collection?

sobes.tech AI

Answer from AI

To avoid ConcurrentModificationException when iterating over a collection, you can use the following approaches:

  1. Use thread-safe collections: java.util.concurrent provides thread-safe alternatives to standard collections. For example:

    • CopyOnWriteArrayList instead of ArrayList
    • CopyOnWriteArraySet instead of HashSet
    • ConcurrentHashMap instead of HashMap
    // Example with CopyOnWriteArrayList
    import java.util.concurrent.CopyOnWriteArrayList;
    import java.util.Iterator;
    
    public class ConcurrentCollectionExample {
        public static void main(String[] args) {
            CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();
            list.add("A");
            list.add("B");
            list.add("C");
    
            // Iterator does not throw ConcurrentModificationException
            Iterator<String> iterator = list.iterator();
            while (iterator.hasNext()) {
                String element = iterator.next();
                System.out.println(element);
                // Modifying the collection during iteration is safe
                if (element.equals("B")) {
                    list.remove(element);
                }
            }
            System.out.println("Final list: " + list);
        }
    }
    
  2. Lock the collection: Synchronize access to the collection using synchronized or locks from java.util.concurrent.locks.

    // Example with synchronized block
    import java.util.ArrayList;
    import java.util.Iterator;
    import java.util.List;
    
    public class SynchronizedCollectionExample {
       public static void main(String[] args) {
           List<String> list = new ArrayList<>();
           list.add("A");
           list.add("B");
           list.add("C");
    
           synchronized (list) { // Lock the collection for access
               Iterator<String> iterator = list.iterator();
               while (iterator.hasNext()) {
                   String element = iterator.next();
                   System.out.println(element);
                   // Cannot modify the collection here if another thread might access it
                   // through the same synchronized block
               }
           }
           // Modifying the collection outside the synchronized block or from another thread
           // may still cause ConcurrentModificationException during parallel iteration
       }
    }
    
  3. Use iterator for removal: If you need to remove elements during iteration, use the remove() method of the iterator (if supported by the collection).

    // Example of removal using Iterator.remove()
    import java.util.ArrayList;
    import java.util.Iterator;
    import java.util.List;
    
    public class IteratorRemoveExample {
        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 element = iterator.next();
                System.out.println(element);
                // Safe removal of element via iterator
                if (element.equals("B")) {
                    iterator.remove();
                }
            }
            System.out.println("Final list: " + list);
        }
    }
    
  4. Iterate over a copy of the collection: Create a copy of the collection and iterate over it. Changes to the original collection will not affect the iteration over the copy.

    // Example of iterating over a copy
    import java.util.ArrayList;
    import java.util.List;
    
    public class CopyIterationExample {
        public static void main(String[] args) {
            List<String> list = new ArrayList<>();
            list.add("A");
            list.add("B");
            list.add("C");
    
            // Create a copy for iteration
            List<String> copyList = new ArrayList<>(list);
    
            for (String element : copyList) {
                System.out.println(element);
                // Can modify the original collection during iteration over the copy
                if (element.equals("B")) {
                    list.remove(element);
                }
            }
            System.out.println("Final list: " + list);
        }
    }
    

The choice of approach depends on specific requirements: whether you need to modify the collection during iteration, the importance of performance, and whether the code runs in a multithreaded environment. CopyOnWriteArrayList/CopyOnWriteArraySet are suitable when reads significantly outnumber writes. Locking is appropriate when modifications are less frequent but may be more frequent than with CopyOnWrite. Using Iterator.remove() or iterating over a copy is applicable when modifications occur only within a single iteration or thread.