从ArrayList
源代码(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();
}
}
对 an 的每次修改操作ArrayList
都会增加modCount
字段(列表自创建以来被修改的次数)。
创建迭代器时,它将当前值存储modCount
到中expectedModCount
。逻辑是:
- 如果列表在迭代过程中根本没有被修改,
modCount == expectedModCount
- 如果列表被迭代器自己的
remove()
方法修改,modCount
则增加,但也expectedModCount
增加,因此modCount == expectedModCount
仍然成立
- 如果其他方法(甚至其他迭代器实例)修改列表,则
modCount
增加,因此modCount != expectedModCount
,这会导致ConcurrentModificationException
但是,正如您从源代码中看到的那样,检查不是在hasNext()
方法中执行的,而是在next()
. 该hasNext()
方法也仅将当前索引与列表大小进行比较。当您从列表 ( "February"
) 中删除倒数第二个元素时,这将导致以下调用hasNext()
简单地返回false
并在 CME 可能被抛出之前终止迭代。
但是,如果您删除了倒数第二个以外的任何元素,则会引发异常。