遍历List集合和Map进行修改和删除报java.util.ConcurrentModificationException错误详解

一、异常产生
当我们使用foreach迭代一个ArrayList或者HashMap时,如果尝试对集合做一些修改操作(例如删除元素或新增),可能会抛出java.util.ConcurrentModificationException的异常。


```java
 public static void main(String[] args) {
        List<User> list=new ArrayList<>();
        for(int i=0;i<10;i++){
            User user = new User();
            user.setMsg("123"+i);
            user.setName("王总"+i);
            list.add(user);
        }
        list.forEach(item->{
            if(Objects.equals(item.getMsg(),"1234")){
                User user = new User();
                item.setName("456789");
                CglibUtil.copy(item,user);
               list.add(user);
            }
        });
        System.out.println(list);
    }

执行之后会报:
在这里插入图片描述


map的例子:

```java
 jcItemMap.forEach((x,items)->{
            List<FinFreightItemR> finFreightItemRList = items.stream()
                    .filter(item -> Objects.equals(item.getAmountFlag(), FinConstant.YesOrNo.YES)).collect(Collectors.toList());
            if(CollectionUtil.isEmpty(finFreightItemRList)){
                jcItemMap.remove(x);
                allItemMap.remove(x);
            }
        });

在这里插入图片描述

二、java.util.ConcurrentModificationException异常产生的原因
ArrayList的父类AbstarctList中有一个域modCount,每次对集合进行修改(增添元素,删除元素。。。)时都会modCount++.而foreach的背后实现原理其实就是Iterator,等同于注释部分代码。在这里,迭代ArrayList的Iterator中有一个变量expectedModCount,该变量会初始化和modCount相等,但如果接下来对集合进行修改,modCount改变,就会造成expectedModCount !=modCount,此时就会掏出异常java.util.ConcurrentModificationException异常。

过程如下图:
在这里插入图片描述
三、异常的解决
1.单线程环境
上面我们已经了解了异常的发送原因,接下我们说一下解决方案。
1.1我们可以使用iterator迭代器进行遍历

 Iterator<User> iterator = list.iterator();
        while(iterator.hasNext()){
            User user = iterator.next();
            if(Objects.equals(user.getMsg(),"1234")){
                iterator.remove();
            }
        }
        System.out.println(list);

细心的朋友会发现Itr中的也有一个remove方法,实质也是调用了ArrayList中的remove,但增加了expectedModCount = modCount;保证了不会抛出java.util.ConcurrentModificationException异常。

但是,这个办法的有两个弊端
1.只能进行remove操作,add、clear等Itr中没有。
2.而且只适用单线程环境。

2、多线程环境
方法一:迭代前加锁,解决了多线程问题,但还是不能进行迭代add、clear等操作。

public class Test12 {
    static List<String> list = new ArrayList<String>();

    public static void main(String[] args) {
        list.add("a");
        list.add("b");
        list.add("c");
        list.add("d");

        new Thread() {
            public void run() {
                Iterator<String> iterator = list.iterator();

                synchronized (list) {
                    while (iterator.hasNext()) {
                        System.out.println(Thread.currentThread().getName()
                                + ":" + iterator.next());
                        try {
                            Thread.sleep(1000);
                        } catch (InterruptedException e) {
                            // TODO Auto-generated catch block
                            e.printStackTrace();
                        }
                    }
                }
            };
        }.start();

        new Thread() {
            public synchronized void run() {
                Iterator<String> iterator = list.iterator();

                synchronized (list) {
                    while (iterator.hasNext()) {
                        String element = iterator.next();
                        if (Objects.equals(element,"c")) {
                            System.out.println(Thread.currentThread().getName()
                                    + ":" + element);
                            iterator.remove();
                        }
                    }
                }
            };
        }.start();
    }
}

方法二:采用CopyOnWriteArrayList,解决了多线程问题,同时可以add、clear等操作

public class Test12 {
    static List<String> list = new CopyOnWriteArrayList<>();

    public static void main(String[] args) throws InterruptedException {
        list.add("a");
        list.add("b");
        list.add("c");
        list.add("d");
        new Thread() {
            public void run() {
                Iterator<String> iterator = list.iterator();

                synchronized (list) {
                    while (iterator.hasNext()) {
                        System.out.println(Thread.currentThread().getName()
                                + ":" + iterator.next());
                        try {
                            Thread.sleep(1000);
                        } catch (InterruptedException e) {
                            // TODO Auto-generated catch block
                            e.printStackTrace();
                        }
                    }
                }
            };
        }.start();

        new Thread() {
            public synchronized void run() {
                Iterator<String> iterator = list.iterator();

                synchronized (list) {
                    while (iterator.hasNext()) {
                        String element = iterator.next();
                        if (Objects.equals(element,"c")) {
                            System.out.println(Thread.currentThread().getName()
                                    + ":" + element);
                            list.remove(element);
                            list.add("123456");
                        }
                    }
                }
            };
        }.start();
        Thread.sleep(5000);
        System.out.println(list);
    }
}

CopyOnWriteArrayList也是一个线程安全的ArrayList,其实现原理在于,每次add或remove等所有的操作都是重新创建一个新的数组,再把引用指向新的数组。

对于HashMap的迭代删除是一样的

在这里插入图片描述

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

ChatGPT显现“ Something went wrong. If this issue persists ...”什么原因?如何解决?

一、报错提示 Something went wrong. If this issue persists please contact us through our help center at help.openai.com. 二、解决方案 一般是代理节点出现问题 ChatGPT退出登录 关闭代理并重新启动代理 切换其他节点 清除浏览器缓存 重新登录ChatGPT 三、其它思路…

C# TabControl实现为每一个TabPage添加关闭按钮

默认情况下TabControl是无法通过界面关闭TabPage的 有些情况下我们需要手动关闭任意一个TabPage&#xff0c;如下图所示 TabControl控件自带属性是无法满足以上需求&#xff0c;下面简单介绍实现过程 1、首先需要对TabPage进行重绘&#xff0c;其目的是为了在TabPage上画出…

Linux学习第38天:Linux I2C 驱动实验(二):哥俩好

Linux版本号4.1.15 芯片I.MX6ULL 大叔学Linux 品人间百味 思文短情长 本节笔记主要学习I2C设备驱动编写及硬件原理图分析。 先把整个本节的思维导图贴出来&#xff1a; 二、I.MX6U的I2C适配器驱动分析 适配器驱动一般都是由SOC厂商提…

乌镇峰会十年之约,为什么IBM是那颗最亮的星?

&#xff08;IBM董事长兼首席执行官Arvind Krishna&#xff09; 2023年处于世界互联网下一个50年开端的头五年。54年前的1969年&#xff0c;世界互联网诞生&#xff1b;同年&#xff0c;IBM助力“阿波罗”号登月成功、首次将人类宇航员送上了月球&#xff1b;当时&#xff0c;…

React Native适配Xcode 15 iOS 17.0+

iOS 17.0 Simulator(21A328)下载失败 App Store 更新到 Xcode15 后&#xff0c;无法运行模拟器和真机。需要下载iOS 17对应的模拟器。Xcode中更新非常容易中断失败&#xff0c;可以在官网单独下载iOS 17模拟器文件&#xff0c;例如&#xff1a;iOS_17.0.1_Simulator_Runtime.d…

WPS表格无法粘贴信息,原因是复制区域与粘贴区域形状不同

WPS表格无法粘贴信息&#xff0c;原因是复制区域与粘贴区域形状不同 问题描述 我是选中了一整列&#xff0c;复制&#xff0c;但是无法粘贴到另一个EXCEL表格中 原因 首先我的数据量很大&#xff0c;有20万行&#xff0c;然后需要复制的EXCEL是.xls格式的&#xff0c;.xls格…

ros1 基础学习07 - 模拟客户端生成小乌龟服务请求生成小乌龟

模拟客户端生成小乌龟服务请求生成小乌龟 一、话题模型二、创建功能包三 创建客户端Client代码四 配置CMakeLists.txt编译规则&#xff1a;五 测试启动ros 主服务启动小乌龟的服务启动模型客户端服务 一、话题模型 Sever端是海龟仿真器/turtlesim&#xff0c;Client端是待实现…

leetcode:2485. 找出中枢整数(python3解法)

难度&#xff1a;简单 给你一个正整数 n &#xff0c;找出满足下述条件的 中枢整数 x &#xff1a; 1 和 x 之间的所有元素之和等于 x 和 n 之间所有元素之和。 返回中枢整数 x 。如果不存在中枢整数&#xff0c;则返回 -1 。题目保证对于给定的输入&#xff0c;至多存在一个中…

ChatGPT和API发生重大中断!

11月9日凌晨&#xff0c;OpenAI在官网发布&#xff0c;ChatGPT和API发生重大中断&#xff0c;导致全球所有用户无法正常使用&#xff0c;宕机时间超过2小时。 目前&#xff0c;OpenAI已经找到问题所在并进行了修复&#xff0c;但仍然不稳定&#xff0c;会继续进行安全监控。 …

3.前端调式

1. Elements 先来看这张图最上头的一行是一个功能菜单&#xff0c;每一个菜单都有它相应的功能和使用方法&#xff0c;依次从左往右来看 箭头按钮 用于在页面选择一个元素来审查和查看它的相关信息&#xff0c;当我们在Elements这个按钮页面下点击某个Dom元素时&#xff0c;箭…

[量化投资-学习笔记008]Python+TDengine从零开始搭建量化分析平台-CCI和ATR

目录 1. 指标简介CCIATR 2. 程序编写题外话 1. 指标简介 将这两个指标放在一起&#xff0c;一方面是因为这两个指标都属于摆动指数&#xff0c;可以反应市场的活跃度。 另一方面是因为CCI和ATR与之前提到的EMA,MACD,布林带的三个指标的计算基础不同。之前的三个指标都是以收盘…

Oracle迁移(RAC变单机模式)

1.升级内核 systemctl stop firewalld systemctl disable firewalldrpm --import https://www.elrepo.org/RPM-GPG-KEY-elrepo.org rpm -Uvh http://www.elrepo.org/elrepo-release-7.0-2.el7.elrepo.noarch.rpm yum --enablerepo"elrepo-kernel" list --showduplic…

【MySQL】手把手教你centos7下载MySQL

centos7下载MySQL 前言正式开始卸载不需要的环境&#xff08;如果你之前没有安装过数据库相关的东西可以跳过&#xff09;下载mysql登录mysql登陆⽅法⼀【不⾏就下⼀个】登陆⽅法⼆【不⾏就下⼀个】登录方式三 前言 安装和卸载MySQL都用系统的root权限&#xff0c;更方便一点&…

SQL优化之MySQL执行计划(Explain)及索引失效详解

1、执行计划基础 1.1、执行计划&#xff08;Explain&#xff09;定义 在 MySQL 中可以通过 explain 关键字模拟优化器执行 SQL语句&#xff0c;从而解析MySQL 是如何处理 SQL 语句的。 1.2、MySQL查询过程 客户端向 MySQL 服务器发送一条查询请求服务器首先检查查询缓存&am…

SecoClient接收返回码超时解决

第一步&#xff0c;Windows键进入"设置" 第二步&#xff0c;进入"更新与安全" 第三步&#xff0c;找到恢复&#xff0c;点击“高级启动”下的“立即重新启动”&#xff0c;重启电脑&#xff1a;(此时要记住以下步骤) 第四步&#xff0c;重启后选择“疑难解…

下定决心做增长了吗 ——《增长黑客》读后感1

关注我的公众号&#xff0c;并回复【增长黑客】可以获取我整理的《增长黑客》书籍大纲 前言 年中就看完《增长黑客》这本书了&#xff0c;收获还是蛮多的&#xff0c;但因为各种原因&#xff0c;导致我都没有好好做下总结。 前阵子做完总结之后&#xff0c;我就第一时间给组员…

真是性价比之王,腾讯云这款88元云服务器已经圈粉无数!

你是否曾经想过拥有一台属于自己的云服务器&#xff0c;但是却被高昂的价格和复杂的配置吓到了&#xff1f;现在&#xff0c;腾讯云推出了一款价格亲民、简单易用的88元云服务器&#xff0c;让你的梦想成为现实。腾讯云88元/年云服务器配置见下图&#xff1a; 腾讯云88元服务器…

第十六章 反射与注解

通过java的反射机制&#xff0c;程序员可以更深入的控制程序的运行过程。例如&#xff0c;可在程序运行时对象用户输入的信息进行验证&#xff0c;还可以逆向控制程序的执行过程&#xff0c;讲解了反射&#xff0c;另外java还提供了Annotation注解功能&#xff0c;该功能建立在…

Unreal Engine 学习笔记 (3)—— 导入资源

1.导入FBX文件 打开系统文件管理器按下鼠标左键拖动fbx文件到UE编辑器中松开鼠标左键在弹出对话框FBX导入选项页面中&#xff0c;选择对应的骨骼 重定向骨骼 拖动UE4的walk_strafe_back.fbx文件到UE5编辑器中 在弹出的FBX导入选项对话框中选择UE4对应的骨骼 使用重定向资产…

阿里云AIGC小说生成【必得京东卡】

任务步骤 此文真实可靠不做虚假宣传&#xff0c;绝对真实&#xff0c;可截图为证。 领取任务 链接&#xff08;复制到wx打开&#xff09;&#xff1a;#小程序://ITKOL/1jw4TX4ZEhykWJd 教程实践 打开函数计算控制台 应用->创建应用->人工智能->通义千问 AI 助手-…