-
快速失败机制概述
- 快速失败(fail - fast)是 Java 集合框架中的一种错误检测机制。它用于在遍历集合的过程中,如果集合的结构被修改(除了通过迭代器自身的修改方法),就抛出
ConcurrentModificationException异常,以防止出现不可预测的行为。
- 快速失败(fail - fast)是 Java 集合框架中的一种错误检测机制。它用于在遍历集合的过程中,如果集合的结构被修改(除了通过迭代器自身的修改方法),就抛出
-
以
ArrayList为例结合源码分析-
ArrayList的基本结构与modCount变量- 在
ArrayList的源码中,有一个重要的变量modCount,它用于记录集合结构被修改的次数。每当对ArrayList进行结构修改操作(如add、remove、clear等操作)时,modCount就会增加。例如,在add方法的源码中:
- 在
-
public boolean add(E e) {
ensureCapacityInternal(size + 1); // 确保内部数组容量足够
elementData[size++] = e;
modCount++;
return true;
}
- 同样,在
remove方法中也会更新modCount:
public E remove(int index) {
rangeCheck(index);
modCount++;
E oldValue = elementData(index);
int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index + 1, elementData, index, numMoved);
elementData[--size] = null; // 清除引用,帮助GC
return oldValue;
}
-
迭代器的实现与
expectedModCountArrayList的迭代器是通过内部类实现的,例如Itr类。在迭代器的内部,有一个expectedModCount变量,这个变量在迭代器创建时被初始化为集合的modCount。以下是Itr类的部分源码:
private class Itr implements Iterator<E> {
int cursor; // 下一个元素的索引
int lastRet = -1; // 上一个返回的元素的索引,如果没有则为 - 1
int expectedModCount = modCount;
//...
}
-
迭代过程中的检查机制
- 在迭代器的
next方法中,会先调用checkForComodification方法来检查modCount和expectedModCount是否相等。如果不相等,就会抛出ConcurrentModificationException。checkForComodification方法的源码如下:
- 在迭代器的
final void checkForComodification() {
if (modCount!= expectedModCount)
throw new ConcurrentModificationException();
}
- 例如,在
next方法的部分源码中可以看到这个检查:
@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];
}
-
在
for循环中删除数据报错的原因- 当我们使用
for - each循环遍历ArrayList时(for - each循环在底层会使用迭代器),如果在循环体中直接使用ArrayList的remove方法删除元素,ArrayList的modCount会增加,但迭代器的expectedModCount不变。 - 例如,以下代码会导致异常:
- 当我们使用
ArrayList<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);
for (Integer i : list) {
if (i == 2) {
list.remove(i);
}
}
- 因为在执行
list.remove(i)时,list的modCount被更新,而迭代器的expectedModCount(在迭代器创建时初始化后没有被更新)与modCount不再相等。当迭代器继续执行next操作(for - each循环隐式地调用next操作)时,就会触发checkForComodification方法的检查,抛出ConcurrentModificationException。这就是在for循环内部直接删除数据报错的原因,体现了集合的快速失败机制。