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() 来安全地删除元素。

相关推荐
码农娟几秒前
根据文件路径获取base64照片
java
手机忘记时间7 分钟前
在R语言中如何将列的名字改成别的
java·前端·python
苹果酱05677 分钟前
[数据库之十一] 数据库索引之联合索引
java·vue.js·spring boot·mysql·课程设计
zhojiew14 分钟前
istio in action之流量控制与路由
java·数据库·istio
D_aniel_29 分钟前
排序算法-归并排序
java·排序算法·归并排序
可儿·四系桜1 小时前
WebSocket:实时通信的新时代
java·网络·websocket·网络协议
forestsea1 小时前
Maven 插件机制与生命周期管理
java·maven
七月在野,八月在宇,九月在户1 小时前
maven 依赖冲突异常分析
java·maven
金融数据出海2 小时前
黄金、碳排放期货市场API接口文档
java·开发语言·spring boot·后端·金融·区块链
胡斌附体2 小时前
微服务中 本地启动 springboot 无法找到nacos配置 启动报错
java·spring boot·微服务·yml·naocs yml