Reputation: 2238
I am trying to run the below code:
List<String> l = new ArrayList<String>();
l.add("jim");
l.add("pam");
for(String s : l) {
l.remove(s);
}
But I am not getting any exception. Isn't the for each loop uses Iterator and we cannot change the structure of the List while iterating?
EDIT:
I am getting the ConcurrentModificationException
when the list has 3+ elements.Why is that the case?
Upvotes: 1
Views: 123
Reputation: 45786
The reason you don't get a ConcurrentModificationException
is because your list only has two elements which prevents the implementation from detecting the concurrent modification. Here's the Java 13 Iterator
implementation used by ArrayList
:
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; // prevent creating a synthetic constructor Itr() {} 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(); } } @Override public void forEachRemaining(Consumer<? super E> action) { Objects.requireNonNull(action); final int size = ArrayList.this.size; int i = cursor; if (i < size) { final Object[] es = elementData; if (i >= es.length) throw new ConcurrentModificationException(); for (; i < size && modCount == expectedModCount; i++) action.accept(elementAt(es, i)); // update once at end to reduce heap write traffic cursor = i; lastRet = i - 1; checkForComodification(); } } final void checkForComodification() { if (modCount != expectedModCount) throw new ConcurrentModificationException(); } }
As you can see, it checks for concurrent modification in the next()
method, but not in the hasNext()
method. This is important. If you aren't aware, enhanced for loops use Iterator
s behind the scenes. In other words, your for-each loop is equivalent to:
for (Iterator<String> itr = l.itr(); itr.hasNext(); ) {
String e = itr.next();
l.remove(e);
}
Once your loop removes the first element there's only one element left in the list. However, that element has moved down an index (1 → 0) but the cursor of the iterator has been incremented by one. This makes the cursor equal to the size and thus the loop does not execute a second time. Since the loop doesn't execute a second time the next()
method isn't called again and the check for concurrent modification doesn't occur. This also has the side-effect of leaving the second element in the list.
Once your list has three or more elements the loop does execute a second time which allows it to detect the concurrent modification.
Upvotes: 3