java中集合类forEach删除元素报错:ConcurrentModificationException

news2024/9/21 20:43:54

    如题所示,我们在java开发中,可能会有这样的一种情况,一个集合使用完了,我们想删除里面所有的元素,可能会遍历他们,然后依次调用删除操作。最简单的我们使用forEach遍历。

    示例如下:

public class ListForEachDel {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("name");
        list.add("id");
        System.out.println(list);
        list.forEach((item) -> {
            // todo
            list.remove(item);
        });
        System.out.println(list);
    }
}

    运行直接报错:

[name, id]
Exception in thread "main" java.util.ConcurrentModificationException
	at java.util.ArrayList.forEach(ArrayList.java:1262)
	at com.xxx.coll.ListForEachDel.main(ListForEachDel.java:22)

    这个问题,我们根据错误堆栈信息,点进源码,发现forEach这里会有一个modCount与expectedModCount相等的判断:

    这里方法一进入,就赋值expectedModCount = modCount,在最后如果没有其它操作应该是相等的,但是这里是删除操作,它会修改modCount:

    我们调用了list.remove(item)

 

    在remove(Object o)方法中调用了fastRemove(index)方法:

   fastRemove里面修改了modCount,所以它与expectedModCount不相等了,抛出异常。

   同理,add方法添加元素也是不行的。也就是说在forEach方法体内部不可以使用增加、删除元素的方法。

    那么,使用for循环可以吗?答案是可以的,但是也分情况:

    1、下标动态约束从小到大删除,不会报错,但是删除不干净。

    2、下标静态约束从小到大删除,会报索引越界错误。

    3、下标从大到小删除,可以全部删除,而且不会报错。

   如下所示,是三个元素的集合,在删除过程中,如果下标判断使用list.size()动态约束,那么不会报错,但是最后会保留元素2。如果使用静态len=list.size()判断,那么会随着元素减少而越界。

    代码如下:

List<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);
System.out.println("before : " + list);
int len = list.size();
// i < list.size() 下标判断可以动态改变,不会出现越界问题,但是删不干净
// i < len 会出现下标越界
for (int i = 0; i < list.size(); i++) {
	list.remove(list.get(i));
}
System.out.println("after : " + list);

    不报错,但是元素删除有保留:     

    静态下标约束,报错:

 

    删除前面两个不会报错,最后一个元素的时候,下标增长为2,实际只有一个元素,越界。

   

    下标从大到小删除,正常。

    其实,遍历删除元素,一般推荐使用iterator迭代器方式,如下所示,通过迭代器,正常删除数组元素的代码:

List<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);
System.out.println("before : " + list);
Iterator iterator = list.iterator();
while(iterator.hasNext()) {
	iterator.next();
	iterator.remove();
}
System.out.println("after : " + list);

    运行结果:

 

    这段代码,看着有点奇怪,我们在调用iterator.remove()之前,做了iterator.next()操作,如果不小心,去掉了这句代码会发生什么?

    这是因为在Iter.remove()源码中,每次操作之后,会修改lastRet=-1。

 

    另外,我们在这里可以看到,iterator每次删除之后,会修改expectedModCount=modCount,这样,就不会存在forEach删除那种报错情况。 

     上面说到Iter.remove()会把lastRet修改为-1,这样再次删除就直接报错,那么Iter.next()操作为什么能够修复这个错误,我们看源码便知道原因了:

    在源码这里最后一步,它把lastRet赋值成了下标,只要下标不越界,那么就不会等于-1。

    稍微提一下,就是这里我们调用的是迭代器的remove()方法,如果我们调用的是集合的remove(item)方法会怎么样?

    同样的就报了forEach那种错误,因为第一次删除之后,modCount发生了改变,下一次next的时候,直接就报错。

    有一种特殊情况,就是两个元素的时候,它又不报错了:

 

    这又是什么情况呢?我们要在Iter.hasNext()上找答案:

    这里经历一次删除之后,因为next操作把游标cursor+1了,这时候正好是1,而集合size也从2变为1,所以迭代器判断结束,没有执行可能会报错的代码,只能说这里是一个巧合。 

    最后总结一下集合遍历删除的问题:

    foreach  -> 1 直接报错 ConcurrentModificationException
    iterator  -> 1 无next操作会报错 IllegalStateException 
                       2 有next是正常 
                       3 调用了list.remove(value) 可能不报错,但是还是有问题。
    for         ->  1 下标动态约束从小到大依次删除会删不干净 
                       2 下标静态约束从小到大删除会报越界错误 IndexOutOfBoundsException 
                       3 下标从大到小可以全部删除

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/673909.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

EasyCode代码生成插件-模板分享(基于数据表生成MyBatisPlus格式的dao,service,controller和vue组件)

目录 概述 使用演示 模板代码 实体类pojo 表现层controller 业务层service接口 业务层serviceImpl实现类 持久层dao Vue组件 概述 本片博客用于分享EasyCode的自定义模板&#xff08;模板在篇末&#xff09;&#xff0c;用于简化开发&#xff0c;免去重复性的工作。 …

SQL 基础语句

SQL 基础语句 DDL Data Definition Language 数据定义语言创建 create删除 drop修改 alter清空 truncate show tables ; --查看所有表&#xff1a; drop database db1; --删除数据库 create database db1 default character set utf8; --创建数据库 use databas…

【统信uos-server-20-1060e】-详细安装openGauss

【统信uos-server-20-1060e】-详细安装openGauss &#x1f53b; 前言&#x1f53b; 一、安装前准备&#x1f530; 1.1 openGauss安装包下载&#x1f530; 1.2 安装环境准备⛳ 1.2.1 硬件环境要求⛳ 1.2.2 软件环境要求⛳ 1.2.3 软件依赖要求⛳ 1.2.4 关闭操作系统防火墙、selin…

Redis 2023面试5题(四)

一、AOF 持久化&#xff08;Append Only File&#xff09;如何配置&#xff1f; AOF&#xff08;Append Only File&#xff09;持久化是 Redis 的一种持久化方式&#xff0c;它通过记录所有收到的写命令来保存数据。以下是一些关于如何配置 AOF 持久化的重要信息&#xff1a; …

Linux系统下使用移动硬盘或者U盘,如何挂载硬盘分区到Linux系统

本文目录 1、查看当前磁盘分区状态2、查看当前磁盘的挂载状态3、将磁盘挂载到指定目录下4、从文件系统里卸载磁盘 Linux系统里&#xff0c;除根目录以外&#xff0c;任何文件或者目录要想被访问&#xff0c;需要将其“关联”到根目录下的某个目录来实现&#xff0c;这种关联操作…

网络安全等级保护2.0 | 等保合规5件事

网络安全等级保护工作包括定级、备案、安全建设、等级测评、监督检查五个阶段。 1、定级 确认定级对象&#xff0c;参考《定级指南》等初步确认等级&#xff0c;组织专家评审&#xff0c;主管单位审核&#xff0c;公安机关备案审查。 备案 持定级报告和备案表等材料到公安机…

一文读懂openguass dcf网络模块

一文读懂openguass dcf网络模块 文章目录 一文读懂openguass dcf网络模块0. mec概要1. compress2. mec2.1 agent2.1.1 初始化agent2.1.2 agent执行 2.2 channel2.2.1 初始化channel2.2.2 连接channel 2.3 api2.4 func2.5 queue2.5.1 初始化2.5.2 运行2.5.1.1 接收消息入队2.5.1…

基于spss的多元统计分析 之 实例3(血压、胆固醇于心脏病关系的研究)(8/8)

血压、胆固醇于心脏病关系的研究 摘要 一般线性模型中的一种&#xff0c;即反应变量 (dependent variables)为二分类变量的回归分析&#xff0c;模型输出为变量取特定值的概率。 在进行二元Logistic回归分析时&#xff0c;通常会涉及3个步骤&#xff0c;分别是数据处理、卡方分…

自动化运维管理工具——Ansible

目录 一、概述 &#xff08;一&#xff09;特点 &#xff08;二&#xff09;工作特性 二、运行机制 三、安装 &#xff08;一&#xff09;配置源 &#xff08;二&#xff09;安装ansible &#xff08;三&#xff09;查看相关文件 &#xff08;四&#xff09;配置文件 …

如何统计网页访问量

目录 一、搭建Nginx服务 安装Nginx服务 第一步 关闭防火墙和安全机制 第二步 安装扩展包 第三步 安装Nginx和依赖环境 第四步 安装依赖包 第五步 创建一个用户和组 第六步 解包 第七步 进入Nginx目录下编译安装 第八步 进行编译 第九步 添加系统识别操作 第十步 检…

跟朋友撞offer怎么办?接了offer,下个月入职,结果老板面了我朋友,她已经入职了,我的offer还算数吗?...

职场上什么奇葩事都可能发生&#xff0c;跟朋友撞了offer是什么感受&#xff1f; 一位网友求助&#xff1a; 接了offer&#xff0c;正在和现公司谈判离职&#xff0c;下个月才能入职。结果老板面了其他人&#xff0c;正好是楼主认识的人&#xff0c;比楼主大十几岁。更尴尬的是…

浅谈C++|引用篇

目录 引入 一.引用的基本使用 (1)引用的概念&#xff1a; (2)引用的表示方法 (3)引用注意事项 (4)引用权限 二.引用的本质 三.引用与函数 (1)引用做函数参数 (2)引用做函数返回值 四.常量引用 五.引用与指针 引入 绰号&#xff0c;又称外号&#xff0c;是人的本名以外…

基于深度学习的目标检测的介绍(Introduction to object detection with deep learning)

物体检测的应用已经深入到我们的日常生活中&#xff0c;包括安全、自动车辆系统等。对象检测模型输入视觉效果(图像或视频)&#xff0c;并在每个相应对象周围输出带有标记的版本。这说起来容易做起来难&#xff0c;因为目标检测模型需要考虑复杂的算法和数据集&#xff0c;这些…

基于spss的多元统计分析 之 主成分分析(5/8)

实验目的&#xff1a; 1&#xff0e;掌握主成分分析的基本思想&#xff1b; 2&#xff0e;熟悉掌握SPSS软件进行主成分分析的基本操作&#xff1b; 3&#xff0e;利用实验指导的实例数据&#xff0c;上机熟悉主成分分析方法&#xff0e; 实验内容&#xff1a; 下表是我国2005年…

【C++篇】OOP中部分:继承和派生

友情链接&#xff1a;C/C系列系统学习目录 知识总结顺序参考C Primer Plus&#xff08;第六版&#xff09;和谭浩强老师的C程序设计&#xff08;第五版&#xff09;等&#xff0c;内容以书中为标准&#xff0c;同时参考其它各类书籍以及优质文章&#xff0c;以至减少知识点上的…

2023年05月份青少年软件编程Python等级考试试卷六级真题(含答案)

2023-05 Python六级真题 分数&#xff1a;100 题数&#xff1a;38 测试时长&#xff1a;60min 一、单选题(共25题&#xff0c;共50分) 1. 明明每天坚持背英语单词&#xff0c;他建立了英语单词错题本文件“mistakes.txt”&#xff0c;将每天记错的单词增加到该文件中&#x…

ROS:launch文件演示

目录 前言一、添加launch文件夹二、新建launch文件三、编辑launch内容四、 执行文件 前言 一个程序中可能需要启动多个节点&#xff0c;比如:ROS 内置的小乌龟案例&#xff0c;如果要控制乌龟运动&#xff0c;要启动多个窗口&#xff0c;分别启动 roscore、乌龟界面节点、键盘…

【数据分享】1929-2022年全球站点的逐日降雪深度数据(Shp\Excel\12000个站点)

气象数据是在各项研究中都经常使用的数据&#xff0c;气象指标包括气温、风速、降水、能见度等指标&#xff0c;说到气象数据&#xff0c;最详细的气象数据是具体到气象监测站点的数据&#xff01; 对于具体到监测站点的气象数据&#xff0c;之前我们分享过1929-2022年全球气象…

感应电动机起动动态计算(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

#家庭网络配置(华为路由与交换机)

#家庭网络配置 #三层交换机配置 #打开HDCP功能 #进入系统视图 <Huawei>system-view #关闭系统提示信息 [Huawei]undo info-center enable #打开DHCP 使能 [Huawei]dhcp enable #创建vlan 10 vlan 20 vlan 2 [Huawei]vlan batch 2 10 20 #进入 vlan 并配置ip 与 dhcp […