Iterating through a Collection without ConcurrentModificationException

Introduction:

When working with collections in Java, it is common to iterate through the elements of a collection and perform some operations on them. However, there is a common issue that developers face when trying to remove elements from a collection while iterating through it, known as ConcurrentModificationException. This exception is thrown when a collection is modified (i.e., elements added or removed) while it is being iterated through.

Understanding ConcurrentModificationException:

ConcurrentModificationException is a runtime exception that is specifically designed to be thrown when an object is structurally modified while it is being iterated through. It is often caused by attempting to remove elements from a collection using the remove() method of the collection while iterating through it using an enhanced for loop or an iterator. Here's an example that demonstrates this issue:


                public static void main(String[] args) {
                    Collection l = new ArrayList<>();

                    for (int i = 0; i < 10; ++i) {
                        l.add(4);
                        l.add(5);
                        l.add(6);
                    }

                    for (int i : l) {
                        if (i == 5) {
                            l.remove(i); // Throws ConcurrentModificationException
                        }
                    }

                    System.out.println(l);
                }
            

In the above example, we are trying to remove elements from the collection 'l' while iterating through it using an enhanced for loop. However, this code throws a ConcurrentModificationException, indicating a structural modification while iterating issue.

The Reason behind ConcurrentModificationException:

The reason behind this exception is that both the enhanced for loop and the iterator keep track of the internal structure of the collection in order to ensure proper iteration. When an element is removed using the remove() method of the collection, it changes the internal structure of the collection, causing the iterator or enhanced for loop to lose track of its position.

Solutions to Avoid ConcurrentModificationException:

There are several approaches to avoid ConcurrentModificationException when removing elements from a collection while iterating. Some of the commonly used solutions include:

Approach 1: Using Iterator

One of the most recommended approaches to avoid ConcurrentModificationException is by using an iterator explicitly instead of using an enhanced for loop. Here's an example that demonstrates this approach:


                public static void main(String[] args) {
                    Collection l = new ArrayList<>();

                    for (int i = 0; i < 10; ++i) {
                        l.add(4);
                        l.add(5);
                        l.add(6);
                    }

                    Iterator iterator = l.iterator();
                    while (iterator.hasNext()) {
                        int i = iterator.next();
                        if (i == 5) {
                            iterator.remove(); // Removes the element safely
                        }
                    }

                    System.out.println(l);
                }
            

In this example, we explicitly define an iterator for the collection 'l'. We use the iterator's remove() method to safely remove the element from the collection while iterating through it. By using an iterator, we can modify the collection safely without throwing a ConcurrentModificationException.

Approach 2: Using Iterator with a Copy of the Collection

Another approach to avoid ConcurrentModificationException is by creating a copy of the collection and iterating through the copy while removing elements from the original collection using an iterator. Here's an example:


                public static void main(String[] args) {
                    Collection l = new ArrayList<>();

                    for (int i = 0; i < 10; ++i) {
                        l.add(4);
                        l.add(5);
                        l.add(6);
                    }

                    List copy = new ArrayList<>(l); // Create a copy of the collection
                    Iterator iterator = copy.iterator();

                    while (iterator.hasNext()) {
                        int i = iterator.next();
                        if (i == 5) {
                            l.remove(i); // Safely removes the element from the original collection
                        }
                    }

                    System.out.println(l);
                }
            

In this example, we create a copy of the collection 'l' using the constructor of the ArrayList class. We then iterate through the copy using an iterator, but remove elements from the original collection 'l'. This allows us to avoid ConcurrentModificationException while still removing elements from the collection.

Approach 3: Using CopyOnWriteArrayList

Another way to avoid ConcurrentModificationException is to use the CopyOnWriteArrayList class instead of the ArrayList class. CopyOnWriteArrayList is a thread-safe variant of the ArrayList class, which means it can be safely modified while being iterated through without throwing a ConcurrentModificationException. Here's an example:


                import java.util.concurrent.CopyOnWriteArrayList;

                public static void main(String[] args) {
                    Collection l = new CopyOnWriteArrayList<>();

                    for (int i = 0; i < 10; ++i) {
                        l.add(4);
                        l.add(5);
                        l.add(6);
                    }

                    for (int i : l) {
                        if (i == 5) {
                            l.remove(i); // Removes the element safely
                        }
                    }

                    System.out.println(l);
                }
            

In this example, we use the CopyOnWriteArrayList class for the collection 'l' instead of the ArrayList class. CopyOnWriteArrayList handles modifications during iteration by making a fresh copy of the internal array whenever the collection is modified. This allows us to avoid ConcurrentModificationException and safely remove elements from the collection.

Approach 4: Using Stream and Filter

Another approach to safely remove elements from a collection while iterating through it is by using Java 8 Streams and the filter() method. Here's an example:


                import java.util.ArrayList;
                import java.util.Collection;

                public static void main(String[] args) {
                    Collection l = new ArrayList<>();

                    for (int i = 0; i < 10; ++i) {
                        l.add(4);
                        l.add(5);
                        l.add(6);
                    }

                    l = l.stream()
                            .filter(i -> i != 5)
                            .collect(Collectors.toList());

                    System.out.println(l);
                }
            

In this example, we use the filter() method of the Stream interface to create a new collection that excludes the elements we want to remove. By collecting the filtered stream into a new list, we effectively remove the desired elements from the collection without throwing a ConcurrentModificationException.

Conclusion:

When removing elements from a collection while iterating through it, it is important to avoid ConcurrentModificationException. This exception is thrown when a collection is modified while being iterated through, causing the iterator or enhanced for loop to lose track of its position. To avoid this exception, you can use approaches like using an iterator explicitly, creating a copy of the collection, using CopyOnWriteArrayList, or using Java 8 Streams and the filter() method. Each approach has its own advantages and disadvantages, so choose the one that best suits your specific requirements.

By following these solutions, you can safely remove elements from a collection without encountering ConcurrentModificationException and ensure smooth execution of your code.