源码角度分析Java 循环中删除数据为什么会报异常
一、源码角度分析Java 循环中删除数据为什么会报异常
相信大家在之前或多或少都知道 Java
中在增强 for
中删除数据会抛出:java.util.ConcurrentModificationException
异常,例如:如下所示程序:
public class RmTest {public static void main(String[] args) {List<String> list = new ArrayList<>();list.add("001");list.add("002");list.add("003");list.add("004");for (String l : list) {if (Objects.equals(l, "002") || Objects.equals(l,"003")) {list.remove(l);}}System.out.println(list);}
}
运行后会发现抛出了异常:
特别是一些新手小伙伴一不注意就陷入其中,当然解决方法也特别简单,可以转为迭代器,然后使用迭代器的 remove
方式删除数据,或者使用循环下标的方式通过下标进行删除,但需要注意正向循环和反向循环,如果是正向循环的话需要注意计算下标位置,不过不要担心,下面我们都会一一进行介绍。
首先来分析下为什么在增强 for
中会出现java.util.ConcurrentModificationException
异常,这里现将java
编译成class
形式,看增强 for
最终是以何种形式执行的:
javac RmTest.java
编译后的内容如下:
public class RmTest {public RmTest() {}public static void main(String[] var0) {ArrayList var1 = new ArrayList();var1.add("001");var1.add("002");var1.add("003");var1.add("004");Iterator var2 = var1.iterator();while(true) {String var3;do {if (!var2.hasNext()) {System.out.println(var1);return;}var3 = (String)var2.next();} while(!Objects.equals(var3, "002") && !Objects.equals(var3, "003"));var1.remove(var3);}}
}
可以看到增强for
最终是编译成迭代器的方式进行遍历数据,但需要注意的是删除数据依然使用的 List
中的 remove
方法,通过抛出的异常链可以看出,问题发生在了 next
方法中的 checkForComodification
方法下:
下面看到 ArrayList
下迭代器的 next
方法中,在 Itr
类下:
在这个方法中首先调用了 checkForComodification
方法,正好上面的异常链中也涉及到了 checkForComodification
方法,下面进到该方法中:
这里是不是看到了熟悉的 ConcurrentModificationException
异常,只要 modCount
和 expectedModCount
不相等就会抛出该异常,下面看下 expectedModCount
的声明位置:
在迭代器内部声明的,并且起始值等于 modCount
,而 modCount
则在定义在 AbstractList
在迭代器的外部,这里还记得前面迭代器中使用的是 List
中的 remove
方法删除的数据,这里看到该方法中:
该方法实际的删除逻辑在 fastRemove
方法中,继续看到该方法下:
看到这里是不是很直观了,modCount
数值发生了变化,而迭代器中的expectedModCount
没有随之修改,就导致 expectedModCount != modCount
而抛出异常。
我们都知道使用迭代器中的 remove
方式是不会引发异常的,比如:
public class RmTest {public static void main(String[] args) {List<String> list = new ArrayList<>();list.add("001");list.add("002");list.add("003");list.add("004");Iterator<String> iterator = list.iterator();while (iterator.hasNext()) {String l = iterator.next();if (Objects.equals(l, "002") || Objects.equals(l, "003")) {iterator.remove();}}System.out.println(list);}}
运行结果:
为什么迭代器的 remove
可以呢,下面看到该方法中:
可以看出迭代器的 remove
同样也是使用了 List
中的 remove
方法,但它会在删除后重置 expectedModCount
的值,使其保持和 modCount
一致,因此就不会触发上面的异常。
看到这里应该明白为什么会抛出异常了,但为什么这样设计呢?这里可以总结下其中,modCount
主要表示集合被修改的次数,expectedModCount
表示迭代器内部维护的集合被修改的次数。当modCount
和expectedModCount
不相等时,则表示肯定有其他某个地方对集合进行了修改,此时,如果继续使用迭代器遍历集合,就可能会出现遍历到非预期的元素或者下个元素不存在了,因此只要expectedModCount
和modCount
保持一致,数据就可认为是可信的。
通过这里也能给我们警醒,如果需要在并发情况下操作集合一定要选用线程安全的集合。
下面再补充下如果不用增强for
,使用下标自增的方式删除是否可行吗?
public class RmTest {public static void main(String[] args) {List<String> list = new ArrayList<>();list.add("001");list.add("002");list.add("003");list.add("004");for (int i = 0; i < list.size(); i++) {String l = list.get(i);if (Objects.equals(l, "002") || Objects.equals(l,"003")) {list.remove(i);}}System.out.println(list);}
}
运行后:
发现 003
并没有被移除,因为当移除了 002
后,002
后的数据顺势向前移位,原本003
的下标为 2
,移位后变成了 1
,但下标 i
继续增长,便会错过后面的数据,那怎么解决呢,既然后面的数据向前移位,对下标i
也向前移位就是了:
public class RmTest {public static void main(String[] args) {List<String> list = new ArrayList<>();list.add("001");list.add("002");list.add("003");list.add("004");for (int i = 0; i < list.size(); i++) {String l = list.get(i);if (Objects.equals(l, "002") || Objects.equals(l,"003")) {list.remove(i);i = i-1;}}System.out.println(list);}
}
运行后数据正常:
既然正向遍历下标需要移位,那如果反过来反向循环不就可以不管下标了吗:
public class RmTest {public static void main(String[] args) {List<String> list = new ArrayList<>();list.add("001");list.add("002");list.add("003");list.add("004");for (int i = list.size() - 1; i >= 0; i--) {String l = list.get(i);if (Objects.equals(l, "002") || Objects.equals(l, "003")) {list.remove(i);}}System.out.println(list);}
}
运行后数据正常:
相关文章:

源码角度分析Java 循环中删除数据为什么会报异常
一、源码角度分析Java 循环中删除数据为什么会报异常 相信大家在之前或多或少都知道 Java 中在增强 for中删除数据会抛出:java.util.ConcurrentModificationException 异常,例如:如下所示程序: public class RmTest {public sta…...

leetCode 229. 多数元素 II + 摩尔投票法 + 进阶 + 优化空间
229. 多数元素 II - 力扣(LeetCode) 给定一个大小为 n 的整数数组,找出其中所有出现超过 ⌊ n/3 ⌋ 次的元素。 进阶:尝试设计时间复杂度为 O(n)、空间复杂度为 O(1)的算法解决此问题。 (1)哈希表 class …...

5 个编写高效 Makefile 文件的最佳实践
在软件开发过程中,Makefile是一个非常重要的工具,它可以帮助我们自动化构建、编译、测试和部署。然而,编写高效的Makefile文件并不是一件容易的事情。在本文中,我们将讨论如何编写高效的Makefile文件,以提高我们的开发…...
20231028刷题记录
P3381 【模板】最小费用最大流 Portal. sol. 注意 SPFA 找最小费用增广路时不要到终点就返回,因为到终点的路径可能有多条不能确定哪条是费用最小的。 P2740 [USACO4.2] 草地排水Drainage Ditches Portal. 最大流模板。 注意区分 N , M N,M N,M。 CF609D G…...
39 深度学习(三):tensorflow.data模块的使用(基础,可跳)
文章目录 data模块的使用基础api的介绍csv文件tfrecord data模块的使用 在训练的过程中,当数据量一大的时候,我们纯读取一个文件,然后每次训练都调用相同的文件,然后进行处理是很不科学的,或者说,当我们需…...

css四种导入方式
1 行内样式 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>Title</title> </head> <body> <h1 style"color: blue">我是标题</h1> </body> </htm…...

Linux学习第24天:Linux 阻塞和非阻塞 IO 实验(一): 挂起
Linux版本号4.1.15 芯片I.MX6ULL 大叔学Linux 品人间百味 思文短情长 在正式开始今天的笔记之前谈一下工作中遇见的一个问题。 本篇笔记主要学习Linux 阻塞和非阻塞 IO 实验,主要包括阻塞和非阻塞简介、等待队列、轮询、…...

037-第三代软件开发-系统音量设置
第三代软件开发-系统音量设置 文章目录 第三代软件开发-系统音量设置项目介绍系统音量设置QML 实现C 实现 总结一下 关键字: Qt、 Qml、 volume、 声音、 GPT 项目介绍 欢迎来到我们的 QML & C 项目!这个项目结合了 QML(Qt Meta-Obj…...

Python 自动化详解(pyautogui)
文章目录 1 概述1.1 第三方库:pyautogui1.2 坐标说明 2 操作对象2.1 鼠标2.1.1 定位2.1.2 移动2.1.3 拖动2.1.4 滚动2.1.5 点击 2.2 键盘2.2.1 输入2.2.2 按键2.2.3 快捷键 2.3 屏幕2.3.1 截图2.3.2 分辨率 2.4 信息提示2.4.1 提示框2.4.2 选择框2.4.3 密码输入2.4.…...

【Linux】第四站:Linux基本指令(三)
文章目录 一、时间相关的指令1.指令简介2.使用 二、cal指令三、find指令 -name1.介绍2.使用 四、grep指令1.介绍2.使用 五、zip/unzip指令1.介绍2.zip的安装3.使用 六、tar指令:打包解包,不打开它、直接看内容1.介绍2.使用 七、bc指令八、uname -r指令1.…...

SpringBoot内置工具类之断言Assert的使用与部分解析
先例举一个service的demo中用来验证参数对象的封装方法,使用了Assert工具类后是不是比普通的 if(xxx) { throw new RuntimeException(msg) } 看上去要简洁多了? 断言Assert工具类简介 断言是一个判断逻辑,用来检查不该发生的情况ÿ…...
如何检测租用的香港服务器是不是CN2线路呢?
CN2,是中国电信新一代融合承载网络,是为电信自身关键业务和具有QoS保证的SLA业务服务的,可以提供高性能的网络指 标,平均单向时延、最大单向时延、单向丢包率等均属于顶尖水平。简单地说,CN2和普通网络,就像…...

Spring Boot进阶(94):从入门到精通:Spring Boot和Prometheus监控系统的完美结合
📣前言 随着云原生技术的发展,监控和度量也成为了不可或缺的一部分。Prometheus 是一款最近比较流行的开源时间序列数据库,同时也是一种监控方案。它具有极其灵活的查询语言、自身的数据采集和存储机制以及易于集成的特点。而 Spring Boot 是…...

Redis(02)| 数据结构-SDS
一、键值对数据库是怎么实现的? 在开始讲数据结构之前,先给介绍下 Redis 是怎样实现键值对(key-value)数据库的。 Redis 的键值对中的 key 就是字符串对象,而 value 可以是字符串对象,也可以是集合数据类型…...

HackTheBox-Starting Point--Tier 0---Preignition
文章目录 一 题目二 实验过程 一 题目 Tags Web、Custom Applications、Apache、Reconnaissance、Web Site Structure Discovery、Default Credentials译文:Web、定制应用程序、Apache、侦察、网站结构发现、默认凭证Connect To attack the target machine, you …...

售货机相关的电路
一、货道选通矩阵电路,类似扫描电路,驱动哪个电机,就打开相应的行线与列线输出 二、MDB纸币器,虽然现在国内都是手机支付,但如果机器还是外销国外还是有用 三、硬币器电路,投币与退币,脉冲信号…...

软考高项(十四)项目沟通管理 ★重点集萃★
👑 个人主页 👑 :😜😜😜Fish_Vast😜😜😜 🐝 个人格言 🐝 :🧐🧐🧐说到做到,言出必行&am…...

Linux多线程服务端编程:使用muduo C++网络库 学习笔记 第五章 高效的多线程日志
“日志(logging)”有两个意思: 1.诊断日志(diagnostic log)。即log4j、logback、slf4j、glog、g2log、log4cxx、log4cpp、log4cplus、Pantheios、ezlogger等常用日志库提供的日志功能。 2.交易日志(trasac…...

利用Pholcus框架提取小红书数据的案例分析
前言 在当今互联网时代,数据的获取和分析变得越来越重要。爬虫技术作为一种数据采集的方法,被广泛涉及各个领域。在本文中,我们将介绍如何使用Python Spark语言和Pholcus框架来实现一本小红书数据爬虫的案例分析。 开发简述 Go语言作为一种…...

超详细Hadoop安装教程(单机版、伪分布式)
超详细Hadoop安装教程(单机版、伪分布式) 1.Hadoop分布式系统基础架构介绍1.1.Hadoop核心 2.Hadoop安装教程2.1.环境准备2.2.配置用户ssh 免密登录2.3.JAVA环境的安装和配置2.4.Hadoop安装2.5.单机版Hadoop配置2.6.伪分布式Hadoop配置2.7Hadoop初始化 1.…...

Docker 离线安装指南
参考文章 1、确认操作系统类型及内核版本 Docker依赖于Linux内核的一些特性,不同版本的Docker对内核版本有不同要求。例如,Docker 17.06及之后的版本通常需要Linux内核3.10及以上版本,Docker17.09及更高版本对应Linux内核4.9.x及更高版本。…...

家政维修平台实战20:权限设计
目录 1 获取工人信息2 搭建工人入口3 权限判断总结 目前我们已经搭建好了基础的用户体系,主要是分成几个表,用户表我们是记录用户的基础信息,包括手机、昵称、头像。而工人和员工各有各的表。那么就有一个问题,不同的角色…...

1.3 VSCode安装与环境配置
进入网址Visual Studio Code - Code Editing. Redefined下载.deb文件,然后打开终端,进入下载文件夹,键入命令 sudo dpkg -i code_1.100.3-1748872405_amd64.deb 在终端键入命令code即启动vscode 需要安装插件列表 1.Chinese简化 2.ros …...

深度学习习题2
1.如果增加神经网络的宽度,精确度会增加到一个特定阈值后,便开始降低。造成这一现象的可能原因是什么? A、即使增加卷积核的数量,只有少部分的核会被用作预测 B、当卷积核数量增加时,神经网络的预测能力会降低 C、当卷…...

基于SpringBoot在线拍卖系统的设计和实现
摘 要 随着社会的发展,社会的各行各业都在利用信息化时代的优势。计算机的优势和普及使得各种信息系统的开发成为必需。 在线拍卖系统,主要的模块包括管理员;首页、个人中心、用户管理、商品类型管理、拍卖商品管理、历史竞拍管理、竞拍订单…...

手机平板能效生态设计指令EU 2023/1670标准解读
手机平板能效生态设计指令EU 2023/1670标准解读 以下是针对欧盟《手机和平板电脑生态设计法规》(EU) 2023/1670 的核心解读,综合法规核心要求、最新修正及企业合规要点: 一、法规背景与目标 生效与强制时间 发布于2023年8月31日(OJ公报&…...
提升移动端网页调试效率:WebDebugX 与常见工具组合实践
在日常移动端开发中,网页调试始终是一个高频但又极具挑战的环节。尤其在面对 iOS 与 Android 的混合技术栈、各种设备差异化行为时,开发者迫切需要一套高效、可靠且跨平台的调试方案。过去,我们或多或少使用过 Chrome DevTools、Remote Debug…...
uniapp 集成腾讯云 IM 富媒体消息(地理位置/文件)
UniApp 集成腾讯云 IM 富媒体消息全攻略(地理位置/文件) 一、功能实现原理 腾讯云 IM 通过 消息扩展机制 支持富媒体类型,核心实现方式: 标准消息类型:直接使用 SDK 内置类型(文件、图片等)自…...
【安全篇】金刚不坏之身:整合 Spring Security + JWT 实现无状态认证与授权
摘要 本文是《Spring Boot 实战派》系列的第四篇。我们将直面所有 Web 应用都无法回避的核心问题:安全。文章将详细阐述认证(Authentication) 与授权(Authorization的核心概念,对比传统 Session-Cookie 与现代 JWT(JS…...
书籍“之“字形打印矩阵(8)0609
题目 给定一个矩阵matrix,按照"之"字形的方式打印这个矩阵,例如: 1 2 3 4 5 6 7 8 9 10 11 12 ”之“字形打印的结果为:1,…...