上一篇文章讲到怎么删除集合里面的元素,其中提到了快速失败原则。
本章将深入底层源码去分析快速失败原则
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("语文");
list.add("数学");
list.add("英语");
System.out.println(list);
Iterator<String> it = list.iterator();
while(it.hasNext()) {
it.remove();
}
System.out.println("删除后" + list);
}
以下代码运行结果为:发生IllegalStateException异常,为什么会这样呢?
ArrayList内维护了一个迭代器类Itr,里面定义了一个初始值为-1的lastRet属性保存当前元素的位置,只有调用其next()方法才会获取当前元素,并给lastRet重新赋值,在删除元素时会检测lastRet是否小于0(是否默认值-1),lastRet小于0抛出IllegalStateException异常。如下图(1)(2)(3)
private class Itr implements Iterator<E> {
int cursor; // 游标,类似于指针、下标
int lastRet = -1; // (1)关键属性
int expectedModCount = modCount; (4)
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]; // (2)数组下标内给lastRet赋值
}
public void remove() {
// (3)删除前检查lastRet是否小于0,小于0抛出异常,只要调用了next()方法就不会小于0
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
ArrayList.this.remove(lastRet);
cursor = lastRet;
lastRet = -1;
expectedModCount = modCount; // (5)modCount的值赋给expectedModCount
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
@Override
@SuppressWarnings("unchecked")
public void forEachRemaining(Consumer<? super E> consumer) {
Objects.requireNonNull(consumer);
final int size = ArrayList.this.size;
int i = cursor;
if (i >= size) {
return;
}
final Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length) {
throw new ConcurrentModificationException();
}
while (i != size && modCount == expectedModCount) {
consumer.accept((E) elementData[i++]);
}
cursor = i;
lastRet = i - 1;
checkForComodification();
}
final void checkForComodification() {
// (6)检查两个变量的值,若不相等,抛出并发修改异常
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
当使用迭代器遍历的方式(foreach以及removeIf()方法都是迭代器遍历),但是没有通过迭代器的remove()删除集合元素时,会导致modCount和expectedModCount两个变量的值不相等,程序抛出ConcurrentModificationException异常。如上图(4)(5)(6)
备注:Set和Map快速失败原则同理,这里就不赘述了~