当前位置: 首页 > news >正文

java数组ArrayList迭代修改的ConcurrentModificationException错误

报错:

Exception in thread "main" java.util.ConcurrentModificationExceptionat java.util.ArrayList$Itr.checkForComodification(ArrayList.java:909)at java.util.ArrayList$Itr.next(ArrayList.java:859)at net.micah.Test.main(Test.java:20)

我写的demo:

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Objects;public class Test {public static void main(String[] args) {List<String> list = new ArrayList<>();list.add("a");list.add("b");list.add("c");list.add("d");list.add("e");list.add("f");// 隐式创建迭代器 Itr(报错:ConcurrentModificationException) for (String next : list) {System.out.println(next);if (Objects.equals(next, "d")) {list.remove(next);}}System.out.println(list);// fori (无法实时拿到数组index更新,导致index对不上,导致错误的修改)for (int i = 0 ; i < list.size(); i++) {String next = list.get(i);System.out.println(next);if(Objects.equals(next, "d") || Objects.equals(next, "e")) {list.remove(next);}}System.out.println(list);// 显式迭代器(安全可靠)Iterator<String> iterator = list.iterator();while (iterator.hasNext()) {String next = iterator.next();System.out.println(next);if (Objects.equals(next, "d")) {iterator.remove();}}System.out.println(list);}}

迭代器内部是闭环的,校验逻辑为:

        final void checkForComodification() {if (modCount != expectedModCount)throw new ConcurrentModificationException();}

expectedModCount 是在创建 Itr(迭代器内部类) 的时候赋值,为那个瞬间的 modCount

private class Itr implements Iterator<E> {int cursor;       // index of next element to returnint lastRet = -1; // index of last element returned; -1 if no suchint expectedModCount = modCount;Itr() {}

① 从源代码上说,这个 expectedModCount 只在迭代器的内部方法调用时被修改,而且 modCount + 1 时 expectedModCount 会和 modCount 保持同步。
也就是说,一旦创建了迭代器(包含 foreach 的情况),就只能调用 Itr 给的方法进行 “modify”,不然下次 checkForComodification 检查必定过不了。(modCount + 1 了,但是 expectedModCount 没有变,导致不相等)

② 从设计原则上说,迭代器一旦生成,就对应生成了游标(cursor),对父类变量(ArrayList.this.elementData)进行遍历操作,你要对父类变量进行变更,特别是触动了数据下标的变化,一定要让我来操作,不然我不知道,迭代的后续操作就不正确(安全)了。这就是“对修改封闭”(开闭原则的闭)。

解决方案:

  1. 用显式迭代器的内部方法进行集合的修改
  2. 处理成新集合,对新集合进行后续操作
  3. fori的反向操作(反向可以减少remove对index变化的影响,但是add操作还是会被影响)

相关文章:

  • linux-awk分析指令
  • 【讯为Linux驱动开发】6.自旋锁spinlock
  • 【LeetCode滑动窗口算法】长度最小的子数组 难度:中等
  • ArcGIS JSAPI 高级教程 - ArcGIS Maps SDK for JavaScript - 探测效果(地图探测、地图窥探)
  • 史上最全盘点:一文告诉你什么是erp?erp系统厂商分别有哪些?
  • 47-3 等保测评报告编写
  • Windows环境下JDK安装及环境变量配置指南
  • 详解 Flink Table API 和 Flink SQL 之时间特性
  • Apache Pulsar 从入门到精通
  • ORACLE中ROWNUM的机制和注意细节(避坑
  • Codeforces Round 952 (Div. 4)
  • 【上海大学计算机组成原理实验报告】七、程序转移机制
  • 网络安全入门教程(非常详细)从零基础入门到精通,看完这一篇你就是网络安全高手了。
  • keil调试过程中遇到的问题及栈分析遇到的问题
  • C++回溯算法
  • JavaScript-如何实现克隆(clone)函数
  • Go 语言编译器的 //go: 详解
  • magento2项目上线注意事项
  • NLPIR语义挖掘平台推动行业大数据应用服务
  • Python代码面试必读 - Data Structures and Algorithms in Python
  • Service Worker
  • 安装python包到指定虚拟环境
  • 二维平面内的碰撞检测【一】
  • 给第三方使用接口的 URL 签名实现
  • 后端_ThinkPHP5
  • 精彩代码 vue.js
  • 看完九篇字体系列的文章,你还觉得我是在说字体?
  • 如何抓住下一波零售风口?看RPA玩转零售自动化
  • 使用 Docker 部署 Spring Boot项目
  • 通过获取异步加载JS文件进度实现一个canvas环形loading图
  • 推荐一款sublime text 3 支持JSX和es201x 代码格式化的插件
  • Mac 上flink的安装与启动
  • 摩拜创始人胡玮炜也彻底离开了,共享单车行业还有未来吗? ...
  • ​​​​​​​​​​​​​​汽车网络信息安全分析方法论
  • ​ubuntu下安装kvm虚拟机
  • ​Z时代时尚SUV新宠:起亚赛图斯值不值得年轻人买?
  • ​浅谈 Linux 中的 core dump 分析方法
  • ​虚拟化系列介绍(十)
  • #FPGA(基础知识)
  • #常见电池型号介绍 常见电池尺寸是多少【详解】
  • (003)SlickEdit Unity的补全
  • (13)[Xamarin.Android] 不同分辨率下的图片使用概论
  • (Redis使用系列) Springboot 整合Redisson 实现分布式锁 七
  • (八十八)VFL语言初步 - 实现布局
  • (二)pulsar安装在独立的docker中,python测试
  • (二刷)代码随想录第15天|层序遍历 226.翻转二叉树 101.对称二叉树2
  • (附源码)spring boot基于Java的电影院售票与管理系统毕业设计 011449
  • (附源码)spring boot建达集团公司平台 毕业设计 141538
  • (附源码)spring boot校园拼车微信小程序 毕业设计 091617
  • (亲测)设​置​m​y​e​c​l​i​p​s​e​打​开​默​认​工​作​空​间...
  • (三)centos7案例实战—vmware虚拟机硬盘挂载与卸载
  • (五) 一起学 Unix 环境高级编程 (APUE) 之 进程环境
  • (五)IO流之ByteArrayInput/OutputStream
  • (转)chrome浏览器收藏夹(书签)的导出与导入
  • (转)Linux下编译安装log4cxx