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

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

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

相关推荐
东阳马生架构5 分钟前
Sentinel源码—7.参数限流和注解的实现一
java·sentinel
正经教主11 分钟前
【问题】解决docker的方式安装n8n,找不到docker.n8n.io/n8nio/n8n:latest镜像的问题
运维·docker·容器·n8n
李白的粉12 分钟前
基于springboot的在线教育系统
java·spring boot·毕业设计·课程设计·在线教育系统·源代码
码农10087号25 分钟前
Hot100方法及易错点总结2
java
iuyou️1 小时前
Spring Boot知识点详解
java·spring boot·后端
北辰浮光1 小时前
[Mybatis-plus]
java·开发语言·mybatis
一弓虽1 小时前
SpringBoot 学习
java·spring boot·后端·学习
南客先生1 小时前
互联网大厂Java面试:RocketMQ、RabbitMQ与Kafka的深度解析
java·面试·kafka·rabbitmq·rocketmq·消息中间件
ai大佬1 小时前
Java 开发玩转 MCP:从 Claude 自动化到 Spring AI Alibaba 生态整合
java·spring·自动化·api中转·apikey
joker_zsl2 小时前
docker的安装和简单使用(ubuntu环境)
运维·docker·容器