Java集合为什么不能使用foreach删除元素

文章目录

  • 前言
  • foreach
  • 为什么不能使用foreach操作
  • ArrayList迭代器
  • 解析

前言

相信各位程序猿在开发的过程中都用过foreach循环,简单快捷的遍历集合或者数组,但是在通过foreach进行集合操作的时候就不可以了,这是为什么?这里先把问题提出来,接着往下看。

foreach

foreach本质是什么?最初出现在JDK 1.5中,也被称为“增强的for循环”。它的设计目的是提供一种简洁、易读的语法,用于遍历集合或数组中的元素,减少了传统 for循环的冗余代码和错误机会。在使用foreach遍历的前提一定是一个数组或者实现了迭代器的集合才行,所以它的底层是使用迭代器去遍历容器内的数据的。

public class TestForDemo {public static void main(String[] args) {List<String> list =new ArrayList<>();list.add("A");list.add("B");list.add("C");list.add("D");for (String s : list) {System.out.println(s);}}
}

看上面这段代码是使用foreach进行集合的遍历的,使用的过程也是在这个循环内使用遍历出来的元素,但是如果我们在这个循环内对集合进行操作,比如增加或者删除,可以看一下结果
在这里插入图片描述
这里判断当元素为D的时候就意味着集合遍历到最后一个元素了,那么我就向集合中添加一个元素E,但是程序报错了,那么正常的方式我们应该使用普通的for循环了对集合进行操作,正确代码是下面这样的。
添加

        for (int i = 0; i < list.size(); i++) {System.out.println(list.get(i));if(list.get(i).equals("D")){list.add("E");}}

移除

        for (int i = 0; i < list.size(); i++) {System.out.println(list.get(i));if(list.get(i).equals("D")){list.remove(i);i--;}}

为什么不能使用foreach操作

这里来带着大家看一下为什么通过foreach来对集合进行添加或者删除这些操作时会保存,首先我们要去看一下集合的内部都有什么,这里以ArrayList为例,其它类型的也是大同小异可以理解一样的。
在这里插入图片描述
这些是我们通过看ArrayList的内部能直接看到的,下面我们在看看集合的remove方法和add方法
add
在这里插入图片描述

remove
在这里插入图片描述
在这里插入图片描述
可以看到在操作集合的时候会去维护一个modCount的成员变量,这个成员变量是在哪呢?它是有ArrayList的父类AbstractList提供的,那么这个变量的意义是干什么的呢,可以看一下注释
在这里插入图片描述
这段内容意思是

Java 集合类中的 “modCount” 字段的作用。“modCount” 是一个记录集合结构修改次数的字段,用于在迭代器中进行并发修改检测。当集合结构发生变化时,"modCount"值会相应增加。如果在迭代过程中发现 “modCount” 发生意外改变,迭代器会快速失败并抛出ConcurrentModificationException 异常。子类可以通过适当地增加 "modCount"来提供快速失败的迭代器,但每次调用 add 或 remove 方法只能使 "modCount"增加一次。如果不需要提供快速失败的迭代器,可以忽略该字段。总之,“modCount” 确保了迭代器在集合结构修改期间的正确行为。

看完这段大概可以理解为什么我们通过foreach对集合进行添加或者删除这些操作的时候为什么会报错了,但是还是要去迭代器中去确认一下的。

ArrayList迭代器

先找到ArrayList的迭代器,通过获取迭代器的方法去看一看使用的哪个迭代器
在这里插入图片描述
逐层去查找发现这个迭代器是在ArrayList内部声明的。
在这里插入图片描述
说到这里就要说一下设计模式中的迭代器模式了,ArrayList提供的迭代器也是实现了Java提供的迭代器接口的,关于迭代器我们知道迭代器是挨个枚举出来的,所以不用关心怎么获取数据。
在这里插入图片描述
那回到我们的ArrayList的迭代器,它遍历的只能是ArrayList集合,这个迭代器中有记录当前遍历到哪一个元素以及下一个元素的信息的
在这里插入图片描述
在这里插入图片描述

  1. cursor(光标):cursor 变量表示下一个要返回的元素的索引。在迭代器的 next() 方法中,cursor 会递增,指向下一个元素。它记录了当前迭代器所在位置的索引。

  2. lastRet(上次返回的索引):lastRet 变量表示上一次调用 next() 方法返回的元素的索引。初始值为 -1,表示没有元素被返回过。在迭代器的 remove() 方法中,会使用 lastRet 来删除上一次返回的元素。

  3. expectedModCount是一个变量,用于在迭代器(Iterator)中跟踪集合类的修改次数。在迭代器初始化时,expectedModCount 会被赋值为集合类的 modCount(修改次数)。modCount 是一个计数器,记录了集合被修改的次数。每当对集合进行增加、删除等操作时,modCount 会递增。

解析

为什么foreach对集合进行添加或者删除会报错,是因为foreach底层使用的迭代器,而我们在这个循环内使用的移除或者添加操作是使用的集合的这两个是有区别的,可以看到迭代器使用集合的remove方法移除元素然后将记录集合修改次数的属性同步给自己的expectedModCount属性来确保一直,但是我们通过foreach进行遍历,移除元素时却使用集合的remove方法那么在遍历过程中就会出现上面那个异常。而这个异常是怎么被抛出呢?是迭代器每次去获取数据以及移除数据时都会调用一个checkForComodification方法,这个方法内部就去比对集合的modCount和迭代器的expectedModCount是否一直,如果一直则正常执行,如果不一致则抛出ConcurrentModificationException
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

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

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

相关文章

正点原子-STM32定时器学习笔记(1)未完待续

1. 通用定时器简介&#xff08;F1为例&#xff09; F1系列通用定时器有4个&#xff0c;TIM2/TIM3/TIM4/TIM5 主要特性&#xff1a; 16位递增、递减、中心对齐计数器&#xff08;计数值&#xff1a;0~65535&#xff09;&#xff1b; 16位预分频器&#xff08;分频系数&#xff…

[晓理紫]AI专属会议截稿时间订阅

AI专属会议截稿时间订阅 关注{晓理紫}&#xff0c;每日更新最新AI专属会议信息&#xff0c;如感兴趣&#xff0c;请转发给有需要的同学&#xff0c;谢谢支持&#xff01;&#xff01; 如果你感觉对你有所帮助&#xff0c;请关注我&#xff0c;每日准时为你推送最新AI专属会议信…

洛谷:P2957 [USACO09OCT] Barn Echoes G

题目描述 The cows enjoy mooing at the barn because their moos echo back, although sometimes not completely. Bessie, ever the excellent secretary, has been recording the exact wording of the moo as it goes out and returns. She is curious as to just how mu…

普通人应该如何使用GPT

现在GPT4推出的GPTs&#xff0c;包含了各个行业方向&#xff0c;比如DALL&#xff08;绘图&#xff09;、Diagrams&#xff08;图标、流程图&#xff09;、KAYAK&#xff08;航旅助手&#xff09;、Murder Mystery Mayhem&#xff08;侦探扮演&#xff09;、Canva&#xff08;设…

C语言-4

排序算法简介 /*学习内容&#xff1a;冒泡排序&#xff08;最基本的排序方法&#xff09;选择排序&#xff08;冒泡的优化&#xff09;插入排序&#xff08;在合适的位置插入合适的数据&#xff09; *//*排序分类&#xff1a;1.内部排序待需要进行排序的数据全部存放到内存中&…

为什么SpringBoot胖Jar不好

公平地说&#xff0c;我有时会怀念 JavaEE 流行的日子。 当然&#xff0c;当时的情况很复杂&#xff0c;但整个 JavaEE 平台设计合理&#xff0c;符合企业开发的需要。 我可以很轻松地将当时的 JavaEE 应用服务器与现代 Kubernetes 架构进行比较&#xff0c;后者现在也有同样…

2024-02-04 混用 C 与 C++ 的 calloc 和 new 导致的问题

点击 <C 语言编程核心突破> 快速C语言入门 混用 C 与 C 的 calloc 和 new 导致的问题 前言一、问题代码二、使用new总结 前言 要解决问题: 同样的代码, 含有std::string的结构, 在gcc环境通过calloc可以赋值, 但是在VS下不行 想到的思路: std::string不是平凡类, 按道…

[职场] C++开发工程师的岗位职责 #学习方法#笔记

C开发工程师的岗位职责 C开发工程师是利用C语言设计完成软件系统底层模块功能&#xff1b;测试软件模块和软集成产品&#xff0c;进行软件故障的诊断、定位、分析和调试&#xff0c;实施产品测试方案&#xff1b;向业务部门提供软件的后期技术支持。C开发工程师是负责使用C编程…

知识融合前沿技术:构建多模态、公平高效的大规模知识表示

目录 前言1 无监督对齐&#xff1a;构建智能实体关联2 多视角嵌入&#xff1a;提高数据利用效率3 嵌入表示增强&#xff1a;挑战节点相似性&#xff0c;对抗训练解决4 大规模实体对齐&#xff1a;克服模糊性和异构性结论 前言 在信息时代&#xff0c;知识融合成为推动人工智能…

全链游戏的未来趋势与Bridge Champ的创新之路

为了充分探索全链游戏的特点和趋势&#xff0c;以及Bridge Champ如何作为一个创新案例融入这一发展脉络&#xff0c;我们需要深入了解这两者之间的互动和相互影响。全链游戏&#xff0c;或完全基于区块链的游戏&#xff0c;代表了游戏行业的一个重要转型&#xff0c;它们利用区…

kafka-splunk数据通路实践

目的&#xff1a; 鉴于目前网络上没有完整的kafka数据投递至splunk教程&#xff0c;通过本文操作步骤&#xff0c;您将实现kafka数据投递至splunk日志系统 实现思路&#xff1a; 创建kafka集群部署splunk&#xff0c;设置HTTP事件收集器部署connector服务创建connector任务&a…

re:从0开始的CSS学习之路 1. CSS语法规则

0. 写在前面 现在大模型卷的飞起&#xff0c;感觉做页面的活可能以后就不需要人来做了&#xff0c;不知道现在还有没有学前端的必要。。。 1. HTML和CSS结合的三种方式 在HTML中&#xff0c;我们强调HTML并不关心显示样式&#xff0c;样式是CSS的工作&#xff0c;现在就轮到C…

6、基于机器学习的预测

应用机器学习的任何预测任务与这四个策略。 文章目录 1、简介1.1定义预测任务1.2准备预测数据1.3多步预测策略1.3.1多输出模型1.3.2直接策略1.3.3递归策略1.3.4DirRec 策略2、流感趋势示例2.1多输出模型2.2直接策略1、简介 在第二课和第三课中,我们将预测视为一个简单的回归问…

EMNLP 2023精选:Text-to-SQL任务的前沿进展(上篇)——正会论文解读

导语 本文记录了今年的自然语言处理国际顶级会议EMNLP 2023中接收的所有与Text-to-SQL相关&#xff08;通过搜索标题关键词查找得到&#xff0c;可能不全&#xff09;的论文&#xff0c;共计12篇&#xff0c;包含5篇正会论文和7篇Findings论文&#xff0c;以下是对这些论文的略…

Redis(三)主从架构、Redis哨兵架构、Redis集群方案对比、Redis高可用集群搭建、Redis高可用集群之水平扩展

转自 极客时间 Redis主从架构 redis主从架构搭建&#xff0c;配置从节点步骤&#xff1a; 1、复制一份redis.conf文件2、将相关配置修改为如下值&#xff1a; port 6380 pidfile /var/run/redis_6380.pid # 把pid进程号写入pidfile配置的文件 logfile "6380.log" …

FPGA解码MIPI视频:Xilinx Artix7-35T低端FPGA,基于MIPI CSI-2 RX Subsystem架构实现,提供工程源码和技术支持

目录 1、前言免责声明 2、相关方案推荐我这里已有的 MIPI 编解码方案本方案在Xilinx Artix7-100T上解码MIPI视频的应用本方案在Xilinx Kintex7上解码MIPI视频的应用本方案在Xilinx Zynq7000上解码MIPI视频的应用本方案在Xilinx Zynq UltraScale上解码MIPI视频的应用纯VHDL代码解…

验证码倒计时:用户界面的小细节,大智慧

欢迎来到我的博客&#xff0c;代码的世界里&#xff0c;每一行都是一个故事 验证码倒计时&#xff1a;用户界面的小细节&#xff0c;大智慧 前言为什么需要验证码倒计时防止滥用&#xff1a;用户心理&#xff1a; 设计考量可见性&#xff1a;友好性&#xff1a;适应性&#xff…

打开/关闭请求回显功能的echo命令

echo命令可用来显示或隐藏DOS状态屏幕显示的内容。在*.bat文件第一行加上echo off&#xff0c;以后的屏幕输出命令&#xff08;包括其他命令产生的提示&#xff09;都会消失&#xff0c;比如在echo off的下一行使用dir&#xff0c;结果是光标原地闪烁&#xff0c;屏幕无显示。使…

安装PyInstaller的保姆级教程

一、安装PyInstaller之前首先要安装Python&#xff0c;小编这里安装的是Python3.9&#xff0c;目前&#xff08;2024/2/6&#xff09;匹配到的最高版本的PyInstaller的版本为6.3.0。需要安装Python的小伙伴可以去这里安装python详细步骤&#xff08;超详细&#xff0c;保姆级&a…

JavaScript运行机制

在web前端开发中&#xff0c;JavaScript无疑是一种非常重要的编程语言。它能够为网页添加动态交互功能&#xff0c;提升用户体验。然而&#xff0c;要充分发挥JavaScript的威力&#xff0c;我们需要对它的运行机制有一定的了解。 JavaScript是一种解释执行的脚本语言&#xff…