ArrayList快速失败机制

news2025/1/16 16:07:31

文章目录

  • 一、什么是快速失败机制
  • 二、例子
  • 三、底层原理
  • 四、解决方法
  • 五、快速失败机制的一个小bug

一、什么是快速失败机制

ArrayList实现了一种称为快速失败(fail-fast)的机制,该机制在并发修改时会抛出ConcurrentModificationException异常。
这种机制的实现原理是:ArrayList在遍历时会记录列表的修改总数(通过modCount字段),如果在遍历过程中列表结构发生变化,那么modCount的值会增大。每次遍历前,迭代器都会检查modCount是否发生改变,如果改变则抛出异常,表示列表并发修改
这个机制的作用是确保在遍历列时候,列表的结构保持稳定,避免并发修改带来未知的结果。

二、例子

        List<String> list = new ArrayList<>();
        list.add("A");
        list.add("B");
        list.add("C");

        for(String s : list){
            if("C".equals(s)){
                list.remove(s);
            }
        }

		System.out.println(list);

在这里插入图片描述
因为在遍历过程中调用 list.remove(s) 修改了列表结构,这会导致modCount的值增加,迭代器在下一步遍历前检查到modCount改变,所以抛出异常。

三、底层原理

AbstractList中定义了modCount并初始化为0,modCount是用来记录修改表结构的次数
在这里插入图片描述

在这里插入图片描述
add操作会将modCount+1
![在这里插入图片描述](https://img-blog.csdnimg.cn/61e2d604564c4d75a9c8237ce7450b12.png
remove方法同样会使modCount++
在这里插入图片描述
看到ArrayList中的迭代器的实现,先将expectModCount的值设为modCount的值,在next()方法中最开始会执行一个checkForComodification()方法
在这里插入图片描述
这个方法的作用就是检查expectedModCount与modCount是否相等,如果在迭代遍历过程中发生了对list结构的修改操作,modCount的值就不会跟expectedModCount相等,然后抛出异常
在这里插入图片描述

二中的例子代码在编译后的结果

        List<String> list = new ArrayList();
        list.add("A");
        list.add("B");
        list.add("C");
        Iterator var2 = list.iterator();

        while(var2.hasNext()) {
            String s = (String)var2.next();
            if ("C".equals(s)) {
                list.remove(s);
            }
        }

        System.out.println(list);

可以看到增强for循环其实本质上就是使用迭代器去遍历

        List<String> list = new ArrayList();
        list.add("A"); // modCount = 1
        list.add("B"); // modCount = 2
        list.add("C"); // modCount = 3
        Iterator var2 = list.iterator(); // expectedModCount = modCount = 3

        while(var2.hasNext()) {
            String s = (String)var2.next(); // 会检查expectedModCount与modCount是否相等
            if ("C".equals(s)) { 
                list.remove(s); // modCount = 4
            }
        }

        System.out.println(list);

这里有人会问,这里明明已经执行到最后一个元素了,会跳出循环,就不会执行next方法,也就不会抛出异常了。看看hasNext()方法的实现
在这里插入图片描述
如果没有删除C元素,此时cursor = size = 3
但是因为remove了一个元素,此时数组大小size变成2,而cursor为3,依旧会进入循环去执行next方法,这次执行就会抛出异常。

四、解决方法

要解决这个问题,我们有几种方式:

  1. 遍历结束后再修改列表结构。这是最简单的方式。
  2. 使用迭代器自己的remove()方法。该方法会判断删除元素是否是迭代器当前元素,如果不是则抛出异常,确保安全。
  3. 使用更高级的并发集合,如CopyOnWriteArrayList。该集合在修改时会返回集合的新副本,确保线程安全。
  4. 使用ListIterator,它支持在遍历过程中安全添加和删除。
  5. 调用removeIf()等在遍历时支持删除的方法。

五、快速失败机制的一个小bug

这里使用的是jdk11

        List<String> list = new ArrayList();
        list.add("A");
        list.add("B");
        list.add("C");
        Iterator var2 = list.iterator();

        while(var2.hasNext()) {
            String s = (String)var2.next();
            if ("B".equals(s)) {
                list.remove(s);
            }
        }

        System.out.println(list);

if (“B”.equals(s)) 当我把C改成B后执行代码的结果
在这里插入图片描述
可以看到神奇的执行成功了,并没有抛出异常。。。
这里算是一个小bug把,ArrayList如果删除的元素刚好是倒数第二个元素时,不会触发快速失败机制
看到编译后的代码执行过程

        List<String> list = new ArrayList();
        list.add("A"); // modCount = 1
        list.add("B"); // modCount = 2
        list.add("C"); // modCount = 3
        Iterator var2 = list.iterator(); // expectedModCount = modCount = 3

        while(var2.hasNext()) {
            String s = (String)var2.next(); // 会检查expectedModCount与modCount是否相等
            if ("B".equals(s)) { 
                list.remove(s); // modCount = 4
            }
        }

        System.out.println(list);

在执行完移除B的操作后,此时数组变成了 [A,C],数组size变成了2,又因为在执行完next方法之后会将迭代器的下标cursor+1指向下一个元素,此时cursor也为2,然后在hasNext方法里判断就会为false,直接跳出了循环(没有执行会抛异常的那个next方法),导致这次代码执行是成功的。

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

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

相关文章

机器学习案例 | 通过EBG学习概念cup

基于解释的学习(explanation-basedlearning)可简称为解释学习&#xff0c;是20世纪80年代中期开始兴起的一种机器学习方法。解释学习根据任务所在领域知识和正在学习的概念知识&#xff0c;对当前实例进行分析和求解&#xff0c;得出一个表征求解过程的因果解释树&#xff0c;以…

spark2

18Spark中stage的划分 和 shuffle的概念 Stage的划分是根据宽依赖&#xff0c;当触发action算子时&#xff0c;按照从后往前的回溯算法&#xff0c;当遇到会发生shuffle算子的时候&#xff0c;就会切分stage。 Stage的划分本质是shuffle,即当遇到会发生shuffle算子的时…

E. Sergey and Subway(思维 + dp)

Problem - E - Codeforces Sergey Semyonovich 是 N 市县的市长&#xff0c;他一直在思考如何进一步改善 Nkers 的生活。不幸的是&#xff0c;几乎所有可以做的事情都已经完成了&#xff0c;白天他已经没有更多的想法&#xff08;他现在喜欢在晚上睡觉&#xff09;。然而&#…

MySQL 中的 distinct 和 group by 的区别

目录 distinct用法distinct多列去重 group by的使用单列去重多列去重 distinct和group by原理隐式排序基于上述原因&#xff0c;Mysql在8.0时&#xff0c;对此进行了优化更新&#xff1a; 结论推荐group by的原因 在语义相同&#xff0c;有索引的情况下&#xff1a;group by和d…

体验 Whisper

体验 Whisper 1. 什么是 Whisper2. Github 地址3. 创建虚拟环境4. 安装 ffmpeg5. 部署 Whisper6. 使用 Whisper 1. 什么是 Whisper Whisper 是一个通用的语音识别模型。它是在一个大型的不同音频数据集上训练出来的&#xff0c;也是一个多任务模型&#xff0c;可以进行多语言语…

MySQL笔记(一):设计范式、基础概念、数据库定义语言DDL

MySQL设计范式、基础概念、数据库定义语言DDL MySQL设计范式、基础概念、数据库定义语言DDL MySQL设计范式、基础概念、数据库定义语言DDL数据模型属性的特点 数据库的创建主键 数据库设计规范第一范式&#xff08;1NF&#xff09;第二范式&#xff08;2NF&#xff09;第三范式…

智能制造之路-从MES到MOM

当前&#xff0c;中国正在大力推动制造业的转型升级&#xff0c;实现由传统制造向智能制造转型。而制造信息化系统&#xff0c;作为支撑企业实现智能制造的一个关键因素&#xff0c;也正越来越多的吸引企业领导人的关注。 某大型制造业集团企业&#xff08;下文简称M集团&…

【Java虚拟机】JVM日志分析和可视化工具实操

1.JVM垃圾GC日志参数配置实战 Java虚拟机中垃圾收集器在运行过程中输出的日志信息主要用于分析垃圾收集器的运行状态、优化垃圾收集器的工作效率以及定位垃圾收集相关的问题GC日志会包含以下内容 垃圾收集器的名称和版本信息。垃圾收集器的运行时间、开始时间和结束时间。垃圾…

【Fluent】利用TUI命令在保存或导出文件时,给文件名加上时间步长、流动时间、迭代步数等求解过程变量的自动编码

一、问题背景 在CSDN的一篇博客&#xff08;从Fluent导出数据到txt文档&#xff09;中&#xff0c;一位博主分享了一串导出求解数据的TUI命令。 file/export/ascii data%t.txt () yes h2s y-velocity x-velocity q no 当时我不知道里面的%t是啥意思&#xff0c;估计是跟时间…

JMeter介绍与安装教程

简介 JMeter是一款开源的负载测试工具&#xff0c;它可以用于测试Web应用程序、API、数据库、负载均衡器等。JMeter 是由 Apache 软件基金会开发的&#xff0c;其目标是提供一个高性能、易于使用的负载测试工具。 JMeter 具有以下特点&#xff1a; 可扩展性&#xff1a;JMet…

又开发好一个系统,详细记录软著申请过程

经过几个通宵达旦的撸代码之后&#xff0c;又开发好了一个系统&#xff0c;和以往一样&#xff0c;系统开发好了少不了申请软件著作权证书&#xff0c;兄弟们&#xff0c;软著申请搞起来啊。 在版权保护中心后台提交资料&#xff0c;没有账号的需要先注册一个账号。很重要的一步…

停车场收费系统

1.系统的开发工具 1.1 AppServe集成应用 Mysql&#xff1a;MySQL 是一款安全、跨平台、高效的&#xff0c;并与 PHP、Java 等主流编程语言紧密结合的数据库系统。该数据库系统是由瑞典的 MySQL AB 公司开发、发布并支持&#xff0c;由 MySQL 的初始开发人员 David Axmark 和 Mi…

【网络技术】什么是CIDR

序言 世界上最幸福的事之一&#xff0c;莫过于经过一番努力后&#xff0c;所有东西正慢慢变成你想要的样子。 文章标记颜色说明&#xff1a; 黄色&#xff1a;重要标题红色&#xff1a;用来标记结论绿色&#xff1a;用来标记一级论点蓝色&#xff1a;用来标记二级论点 1 基础介…

【设计模式】单例模式(懒汉和饿汉模式详解)

目录 1.设计模式是什么&#xff1f; 2.单例模式 1.概念&#xff1a; 2.如何设计一个单例 1.口头约定&#xff08;不靠谱&#xff09; 2.使用编程语言的特性来处理 3.使用"饿汉模式"设计单例 1.详细步骤 2.完整代码 4.使用"饿汉模式"设计单例 1.详…

php+vue在线课程教育学习考试系统864t7

运行环境:phpstudy/wamp/xammp等 开发语言&#xff1a;php 后端框架&#xff1a;Thinkphp5 前端框架&#xff1a;vue.js 服务器&#xff1a;apache 数据库&#xff1a;mysql 数据库工具&#xff1a;Navicat/phpmyadmin功能要求&#xff1a;可以实现首页、个人中心、学生管理、名…

嵌入式软考备考_7 系统开发过程和项目管理

系统开发过程和项目管理 开发模型 把开发过程分成一些阶段。 瀑布模型&#xff1a;SDLC。缺陷在于最开始需求要明确&#xff0c;但是开发周期很难不变动。 因此改进&#xff1a; 原型&#xff1a;一个demo。 快速原型模型&#xff1a;抛弃模型&#xff0c;一旦获取到了用户需…

unity 基本寻径

一、实现效果&#xff1a;敌人追逐玩家&#xff0c;自动躲避障碍物 二、游戏框架 Plane&#xff1a;平面&#xff0c;是玩家和敌人可以行走的区域 Player&#xff1a;玩家&#xff0c;可以在平面上移动&#xff0c;绕开障碍物 Enemy&#xff1a;敌人&#xff0c;可以追逐玩家…

红外遥控且自动避障的嵌入式智能小车系统

访问【WRITE-BUG数字空间】_[内附完整源码和文档] 本次实验使用树莓派作为开发平台&#xff0c;AlphaBot 作为开发载体&#xff0c;Python 作为开发语言&#xff0c;开发一种基于红外线遥控控制的、可自动避障的嵌入式智能小车系统。 智能系统中的嵌入式应用 实验目的 嵌入式…

MySQL双写缓冲区(Doublewrite Buffer)

本文已收录至Github&#xff0c;推荐阅读 &#x1f449; Java随想录 文章目录 摘要为什么需要Doublewrite BufferDoublewrite Buffer原理Doublewrite Buffer相关参数总结 摘要 InnoDB是MySQL中一种常用的事务性存储引擎&#xff0c;它具有很多优秀的特性。其中&#xff0c;Dou…

pip的常用操作命令

1. 忽略已安装的库&#xff08;以llvmlite为例&#xff09; pip install xxx --ignore-installed llvmlite 2. 错误 - AttributeError: int object has no attribute endswith 解决方案&#xff1a; 常见原因因为使用的pip为pip2&#xff0c;可以使用 pip -V 查看pip版本&a…