为什么List、Set集合无法在遍历的时候修改内部元素

ops/2025/3/19 2:00:28/

以常用集合ArrayList为例,ArrayList 在遍历过程中无法直接修改内部元素的结构(例如通过 remove()add() 方法修改元素),是因为 遍历的过程中修改结构 可能会导致 不一致的行为并发修改异常逻辑错误
注意:和 ArrayList一样,Set 集合也不允许在遍历时直接修改其结构(如添加或删除元素),否则会抛出 ConcurrentModificationException 异常。为了安全地修改结构,可以使用 Iteratorremove() 方法或者使用removeIf方法。在需要修改集合结构时,最好先完成遍历操作,避免在遍历过程中进行结构修改,确保程序的稳定性和一致性。

1. ConcurrentModificationException(并发修改异常)

ArrayList 在内部维护了一个修改计数器(modCount),每次修改 ArrayList(如删除或添加元素)时,该计数器会增加。在遍历 ArrayList 时,Iterator 会检查该计数器。如果在遍历过程中,集合的结构发生了变化(例如删除或添加元素),Iterator 会发现计数器的值发生变化,因此抛出 ConcurrentModificationException 异常。

这个机制的目的是为了防止多线程环境下发生集合的并发修改,从而导致无法预料的行为。在单线程情况下,虽然没有并发问题,但仍然需要保持结构一致性。

2. 修改结构导致遍历不一致

在遍历过程中修改 ArrayList 结构(比如添加、删除元素)可能会导致遍历过程中的不一致性。例如:

  • 删除元素: 如果在遍历过程中删除元素,剩下的元素会向前移动,导致后续的元素错位。可能会导致漏掉一些元素。
  • 添加元素: 如果在遍历过程中添加新元素,新的元素会加入到集合的末尾,或者加入到指定位置,这会影响元素顺序,导致遍历时无法保证按预期的顺序访问。

这种行为非常不稳定,并且容易导致程序错误,因此 Java 中的 ArrayList 和其他集合类通常会避免在遍历过程中修改结构。

3. 线程安全性问题

如果 ArrayList 在多个线程中共享使用,且在遍历时对其进行了结构性修改(如添加或删除元素),这可能会导致多个线程间的竞争条件和数据不一致。为了避免此类问题,Java 设计上默认会禁止这种行为,尤其是在多线程环境中。

4. 遍历顺序和数据一致性

遍历 ArrayList 时假定集合的结构是固定的。如果在遍历过程中修改了元素(比如删除或添加元素),会破坏集合的状态,使得遍历结果变得不可预测。为了保证数据一致性,Java 设计者决定不允许在遍历时修改集合的结构。

解决方法

为了在遍历时修改 ArrayList,可以使用以下几种方式:

  • 使用 Iterator 提供的 remove() 方法来安全地删除元素,而不是直接通过 ArrayListremove() 方法:

    java">Iterator<String> iterator = list.iterator();
    while (iterator.hasNext()) {String element = iterator.next();if (element.equals("remove")) {iterator.remove();  // 安全地删除元素}
    }
    
  • 使用 ListIterator,它提供了更多操作,例如修改元素、删除元素等:

    java">ListIterator<String> listIterator = list.listIterator();
    while (listIterator.hasNext()) {String element = listIterator.next();if (element.equals("remove")) {listIterator.remove();  // 删除} else if (element.equals("modify")) {listIterator.set("new value");  // 修改}
    }
    
  • 如果要在遍历过程中添加元素,可以使用 ArrayListadd() 方法,但需要注意添加的元素会被放在遍历结束后,因此可能不在当前遍历中被访问。

  • 在移除元素的时候,使用Java 8以后的特性,代码为例

    java">list.removeIf(item->{int size = new LambdaQueryChainWrapper<>(doctorInformationMapper).eq(DoctorInformation::getKsdm, item.getDepid()).count();return size == 0;});
    

http://www.ppmy.cn/ops/166921.html

相关文章

VSTO(C#)Excel开发12:多线程的诡异

初级代码游戏的专栏介绍与文章目录-CSDN博客 我的github&#xff1a;codetoys&#xff0c;所有代码都将会位于ctfc库中。已经放入库中我会指出在库中的位置。 这些代码大部分以Linux为目标但部分代码是纯C的&#xff0c;可以在任何平台上使用。 源码指引&#xff1a;github源…

Matlab 汽车主动悬架LQR控制器设计与仿真

1、内容简介 Matlab 182-汽车主动悬架LQR控制器设计与仿真 可以交流、咨询、答疑 2、内容说明 略 1、研究背景 汽车悬架系统由弹性元件、导向元件和减振器组成,是车身与车轴之间连接的所有组合体零件的总称,也是车架(或承载式车身)与车桥(或车轮)之间一切力传递装置的总称,…

本地部署github上资源可能出现问题总结

的符号的风格 npm install 可能会出现问题 下载还有可能出现某几个依赖有版本的冲突&#xff0c;我的解决方案是将某高版本的依赖进行回退&#xff0c;在package.json的department里面改了&#xff0c;然后删除node_modules&#xff0c;再重新npm install .这边我下载依赖的时…

SQL注入第6关

存在SQL注入&#xff0c;且闭合为双引号 查询数据库列数&#xff1a;http://127.0.0.1/sqli-labs-master/Less-6/?id1" order by 3-- http://127.0.0.1/sqli-labs-master/Less-6/?id1" order by 4-- 查询可知列数为3 查询数据库库名&#xff1a;http://127.0.0.…

「Unity3D」UGUI运行时设置元素的锚点Anchor,维持元素Rect的显示不变,即待在原处

在编辑器中&#xff0c;通过设置Raw edit mode&#xff0c;可以切换两种&#xff0c;元素锚点的改变模式&#xff1a; 一种是锚点单独改变&#xff0c;即&#xff1a;不开启原始模式&#xff0c;保持原样&#xff0c;改变anchoredPosition与sizeDelta。一种是锚点联动显示&…

【漫话机器学习系列】139.标量(Scalars)

标量&#xff08;Scalars&#xff09;概述 1. 什么是标量&#xff1f; 在数学和计算机科学中&#xff0c;标量&#xff08;scalar&#xff09; 是一种最基本的数据类型&#xff0c;它指的是单个数值&#xff0c;如 4 或 4.02。标量与向量&#xff08;vector&#xff09;、矩阵…

在vue项目中,使用Patch请求,实现根据id修改某张发票的日结状态

目录 前言 一.问题描述 二.后端实现 1.分析 2.检查后端拦截器&#xff0c;看看是否允许接收Patch类型的请求 3.编写Dto 4.编写controller层 5.编写service层 6.mapper层 7.使用apifox&#xff0c;测试后端接口的可用性 三.前端实现 1.封装api&#xff08;本质是ax…

2025深圳国际数字能源展全球招商启动,聚焦能源产业数字化转型

近日&#xff0c;2025深圳国际数字能源展组委会正式宣布&#xff0c;展会将于9月18 - 21日在深圳会展中心盛大举行&#xff0c;目前全球招商已全面启动。该展会旨在汇聚全球数字能源领域的前沿技术与创新成果&#xff0c;推动能源产业的数字化转型&#xff0c;助力构建绿色、高…