迭代器边遍历边删除存在的问题以及原理
01-问题
我们先来看看如下代码
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());}}
我们运行查看结果,可以正常运行输出,如下图所示
上述是可以正常遍历的,我们修改一下代码再来运行
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的源码进行分析
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被别的线程修改,导致出现的问题