Why am I not getting a java.util.ConcurrentModificationException in this example?

Bhesh Gurung picture Bhesh Gurung · Nov 18, 2011 · Viewed 268.2k times · Source

Note: I am aware of the Iterator#remove() method.

In the following code sample, I don't understand why the List.remove in main method throws ConcurrentModificationException, but not in the remove method.

public class RemoveListElementDemo {    
    private static final List<Integer> integerList;

    static {
        integerList = new ArrayList<Integer>();
        integerList.add(1);
        integerList.add(2);
        integerList.add(3);
    }

    public static void remove(Integer toRemove) {
        for(Integer integer : integerList) {
            if(integer.equals(toRemove)) {                
                integerList.remove(integer);
            }
        }
    }

    public static void main(String... args) {                
        remove(Integer.valueOf(2));

        Integer toRemove = Integer.valueOf(3);
        for(Integer integer : integerList) {
            if(integer.equals(toRemove)) {                
                integerList.remove(integer);
            }
        }
    }
}

Answer

pushy picture pushy · Nov 18, 2011

Here's why: As it is says in the Javadoc:

The iterators returned by this class's iterator and listIterator methods are fail-fast: if the list is structurally modified at any time after the iterator is created, in any way except through the iterator's own remove or add methods, the iterator will throw a ConcurrentModificationException.

This check is done in the next() method of the iterator (as you can see by the stacktrace). But we will reach the next() method only if hasNext() delivered true, which is what is called by the for each to check if the boundary is met. In your remove method, when hasNext() checks if it needs to return another element, it will see that it returned two elements, and now after one element was removed the list only contains two elements. So all is peachy and we are done with iterating. The check for concurrent modifications does not occur, as this is done in the next() method which is never called.

Next we get to the second loop. After we remove the second number the hasNext method will check again if can return more values. It has returned two values already, but the list now only contains one. But the code here is:

public boolean hasNext() {
        return cursor != size();
}

1 != 2, so we continue to the next() method, which now realizes that someone has been messing with the list and fires the exception.

Hope that clears your question up.

Summary

List.remove() will not throw ConcurrentModificationException when it removes the second last element from the list.