迭代器边遍历边删除存在的问题以及原理
01-问题
我们先来看看如下代码
java
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
list.add(5);
list.add(4);
list.add(3);
list.add(2);
list.add(7);
list.add(0);
Iterator<Integer> iterator = list.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next());
}
}
我们运行查看结果,可以正常运行输出,如下图所示
上述是可以正常遍历的,我们修改一下代码再来运行
java
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
list.add(5);
list.add(4);
list.add(3);
list.add(2);
list.add(7);
list.add(0);
Iterator<Integer> iterator = list.iterator();
while (iterator.hasNext()){
Integer next = iterator.next();
list.remove(next);
System.out.println(next);
}
}
上述代码我们点击运行,会抛出异常,如下所示
我们来看一下问题所在。
02-原因分析
我们点进去ArrayList获取迭代器的方法,可以看到返回了一个ListItr对象
我们继续点进去,查看ListItr的源码,看到继承了Itr类,我们继续进入
我们进入Itr的源码进行分析
java
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
// 记录版本号信息,modCount是List继承的Abstract的
int expectedModCount = modCount;
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];
}
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
我们首先关注到有三个属性,先来解释一下
- **cursor:**下一个要访问元素的下标索引
- **lastRet:**上一个访问过元素的下标索引
- expectedModCount:我们可以看到这个属性赋值为
modCount
,我们点进去查看,发现其在List
继承的AbstractList
类中,我们翻译一下,可以看到其解释,原来这个是用来记录List被修改的次数,默认是0
在我们使用List的add()、remove()、等方法的时候,会对modCount
进行++的操作,表示该集合被修改的次数,我们注意到在迭代器的next()
中,使用了checkForComodification()
方法,在该方法中,当modCount != expectedModCount
时,会抛出异常,可以看到我们一开始抛出的异常就是这个,原来在我们调用remove方法的时候,修改了modCount ,但是expectedModCount没变,所以导致不一致了。
那为什么要有这个机制呢,这个机制其实是一个类似于乐观锁一样,为了防止并发的时候使用迭代器遍历的时候,List被别的线程修改,导致出现的问题