Why isn't this code causing a ConcurrentModificationException?

According to the Java API docs Iterator.hasNext does not throw a ConcurrentModificationException.

After checking "January" and "February" you remove one element from the list. Calling it.hasNext() does not throw a ConcurrentModificationException but returns false. Thus your code exits cleanly. The last String however is never checked. If you add "April" to the list you get the Exception as expected.

import java.util.List;
import java.util.ArrayList;
import java.util.Iterator;

public class Main {
        public static void main(String args[]) {

                List<String> myList = new ArrayList<String>();
                myList.add("January");
                myList.add("February");
                myList.add("March");
                myList.add("April");

                Iterator<String> it = myList.iterator();
                while(it.hasNext())
                {
                    String item = it.next();
                    System.out.println("Checking: " + item);
                    if("February".equals(item))
                    {
                        myList.remove(item);
                    }
                }

                for (String item : myList)
                {
                    System.out.println(item);
                }

        }
}

http://ideone.com/VKhHWN


From ArrayList source (JDK 1.7):

private class Itr implements Iterator<E> {
    int cursor;       // index of next element to return
    int lastRet = -1; // index of last element returned; -1 if no such
    int expectedModCount = modCount;

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

    @SuppressWarnings("unchecked")
    public E next() {
        checkForComodification();
        int i = cursor;
        if (i >= size)
            throw new NoSuchElementException();
        Object[] elementData = ArrayList.this.elementData;
        if (i >= elementData.length)
            throw new ConcurrentModificationException();
        cursor = i + 1;
        return (E) elementData[lastRet = i];
    }

    public void remove() {
        if (lastRet < 0)
            throw new IllegalStateException();
        checkForComodification();

        try {
            ArrayList.this.remove(lastRet);
            cursor = lastRet;
            lastRet = -1;
            expectedModCount = modCount;
        } catch (IndexOutOfBoundsException ex) {
            throw new ConcurrentModificationException();
        }
    }

    final void checkForComodification() {
        if (modCount != expectedModCount)
            throw new ConcurrentModificationException();
    }
}

Every modifying operation on an ArrayList increments the modCount field (the number of times the list has been modified since creation).

When an iterator is created, it stores the current value of modCount into expectedModCount. The logic is:

  • if the list isn't modified at all during the iteration, modCount == expectedModCount
  • if the list is modified by the iterator's own remove() method, modCount is incremented, but expectedModCount gets incremented as well, thus modCount == expectedModCount still holds
  • if some other method (or even some other iterator instance) modifies the list, modCount gets incremented, therefore modCount != expectedModCount, which results in ConcurrentModificationException

However, as you can see from the source, the check isn't performed in hasNext() method, only in next(). The hasNext() method also only compares the current index with the list size. When you removed the second-to-last element from the list ("February"), this resulted that the following call of hasNext() simply returned false and terminated the iteration before the CME could have been thrown.

However, if you removed any element other than second-to-last, the exception would have been thrown.