List删除异常原理

fori删除

fori 删除导致的异常

会报 indexOutBoundsException

fori循环是通过获取list中object[]数组的下标来循环获取数据的。一旦执行 list.remove(i) List中数组的数据

Object[] elementData 会执行 copy操作,数组index之后的所有数据会往前挪一位,同时把记录数组数量的字段size -1。

在这种情况下,当 i 为 2 时,list.remove(i) 会移除元素 "a",然后 ArrayList 会自动将后续元素前移并更新 size,使得原本在索引位置 2 的元素被移除,导致当前的 i 指向了下一个元素,但我们仍然继续访问原来的 i 索引位置,最终触发 IndexOutOfBoundsException

csharp 复制代码
    List<String> list = new ArrayList<>();
        list.add("b");
        list.add("c");
        list.add("a"); 
//会报查询索引的长度 < 数组的长度
        for (int i = 0; i < list.size(); i++) {
            if (list.get(i).equals("a")) {
                list.remove(i);
            }
            // #1
            System.out.println(list.get(i));
        }

foreach删除

foreach 删除导致的异常

会抛出 ConcurrentModificationException异常

erlang 复制代码
for (String s : list){
    if (s.equals("a")){
       list.remove(s);
    }
}

foreach是语法糖,是为了java程序员加速开发做的优化,因为虚拟机是不认识foreach语法的,经过javac编译之后会变成原本的样子 虚拟机才能执行。

vbnet 复制代码
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
    //#1 这里是关键点  这里会抛出 `ConcurrentModificationException`
    String s = iterator.next();
    if (s.equals("a")) {
        //#2 
        list.remove(s); 
    }
}

Interator是List内部的类,它的next方法如下,他会调用checkForComodification() , 这个方法会在校验expectedModCount与modCount。

arduino 复制代码
  final void checkForComodification() {
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
        }

初始化 Iterator的时候(这里是内部类class Itr)modCount与expectedModCount一致都是更改次数。当调用list.remove()的时候

会调用 fastMove()方法。而这个方法是把modCount++ 增加了1。而下次再循环的时候还会执行 Iterator.next()方法就会导致 expectedModCount与modCount不相等抛异常了。

正确的方式

与上面的区别是 注意#1 位置,不要用list.remove() ,具体为啥上面已经说了。那为什么用iterator.remove就不会报错了了呢?

vbnet 复制代码
        //不会报异常
        Iterator<String> iterator = list.iterator();
        while (iterator.hasNext()) {
            String next = iterator.next();
            if (next.equals("a")) {
                //#1 这里不要用list.remove
                iterator.remove();
            }
        }

iterator.remove()源码如下 注意 #2的位置,这里在删除之后会强行吧 expectedModCount = modCount这2个参数设置成一致,所以再调用.next()就不会抛出不一致的异常了。

csharp 复制代码
 public void remove() {
            if (lastRet < 0)
                throw new IllegalStateException();
            checkForComodification();
            try {
               //调用父类的删除
                ArrayList.this.remove(lastRet);
                cursor = lastRet;
                lastRet = -1;
                // #2
                expectedModCount = modCount;
            } catch (IndexOutOfBoundsException ex) {
                throw new ConcurrentModificationException();
            }
        }

ConcurrentModificationException 是为了避免并发修改引发不一致的状态,这个异常表明在遍历集合时集合结构被修改了(如通过 remove 方法)。Java 的 Iterator 保证在遍历时结构不会被改变,因此需要使用 iterator.remove() 来安全地删除元素。

相关推荐
草履虫建模5 分钟前
RuoYi OpenAPI集成从单体到微服务改造全过程记录
java·运维·vue.js·spring cloud·微服务·云原生·架构
Fireworkitte19 分钟前
接口为什么要设计出v1和v2
java·服务器
bin915336 分钟前
解锁Java开发新姿势:飞算JavaAI深度探秘 #飞算JavaAl炫技赛 #Java开发
java·人工智能·python·java开发·飞算javaai·javaai·飞算javaal炫技赛
橘子编程1 小时前
SpringBoot核心特性详解
java·jvm·spring boot·spring·spring cloud·tomcat
2501_917970032 小时前
主播生活模拟器2|主播人生模拟器2 (Streamer Life Simulator 2)免安装中文版
java·游戏·生活
破刺不会编程2 小时前
linux信号量和日志
java·linux·运维·前端·算法
回家路上绕了弯3 小时前
线程池优化实战:从性能瓶颈到极致性能的演进之路
java·后端
小苏兮3 小时前
飞算JavaAI深度解析:专为Java生态而生的智能引擎
java·开发语言·人工智能·java开发·飞算javaai炫技赛
用户84913717547165 小时前
JDK 17 实战系列(第4期):安全性与稳定性增强详解
java·后端·性能优化
自由的疯5 小时前
java程序员怎么从Python小白变成Python大拿?(三)
java·后端·trae