迭代器边遍历边删除存在的问题

迭代器边遍历边删除存在的问题以及原理

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被别的线程修改,导致出现的问题

相关推荐
xcl092528 分钟前
北京24小时自助健身房系统软件开发实战:从架构到部署全流程指南
java·spring boot·架构
一 乐2 小时前
动漫书销售商城|基于springboot + vue动漫书销售商城(源码+数据库+文档)
java·数据库·vue.js·spring boot·毕业设计
卓怡学长2 小时前
w214基于jsp知道特产网
java·intellij-idea
步行cgn3 小时前
Spring 注解使用详解
java·spring
一条小小yu3 小时前
Spring IoC的理解
java·后端·spring
落魄实习生3 小时前
Agent Scope Java 2.x 系列【7】工具使用
java·开发语言·ai
旺仔学长 哈哈3 小时前
springboot钓鱼爱好者交流平台APP设计与实现
java·spring boot·mysql·充电桩管理系统
bloglin999994 小时前
vmware部署centos7启动docker容器后无法访问服务
运维·docker·容器
自强的小白5 小时前
核心功能(Service接口)
java·mybatis