arraylist 循环删除

arraylist 循环删除经常会有人这么对 list 进行遍历,错而不自知。示例代码如下:public static void main(String[] args) {List<String> list = new ArrayList<>();list.add(“aaa”);lis

经常会有人这么对 list 进行遍历,错而不自知。

示例代码如下:

public static void main(String[] args) {
 List<String> list = new ArrayList<>();
 list.add("aaa");
 list.add("bbb");
 list.add("ccc");
 list.add("ddd");
 for (String str : list) {
 if ("aaa".equals(str)) {
 list.remove("aaa");
 }
 }
}

以上代码执行导致的报错信息如下:

Exception in thread "main" java.util.ConcurrentModificationException
 at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:901)
 at java.util.ArrayList$Itr.next(ArrayList.java:851)
 at demo.service.impl.test.main(test.java:14)

网上有很多博客对此都做了说明,这篇文章通过比较浅显易懂的方式说明报错产生的原因。

一、list.add

list.add 代码执行时,有一个变量发生改变了,那就是 modCount。在代码中 list.add 共执行4次,所以 modCount 的值为 4。

注:list add()、remove() clear() 都会改变 modCount 值。

二、for (String str : list)

for (String str : list) 调用的是 ArrayList 中内部类 ItrItr 是对 Iterator 的实现。而在 Iterator 开始前,会先执行 int expectedModCount = modCount

此时 expectedModCount modCount 均为 4

三、list.remove(“aaa”)

在此处先看一下会报错的原因,以下是源码:

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

modCount expectedModCount 不相等了,所以报错。

有人可能会跟我有一样的想法,为什么 list.remove(“aaa”) 时,不把 expectedModCount = modCount 重新赋值一次。其实是有的,只是调用的方法错了。

例子中 list.remove(“aaa”) 调用的 remove 源码如下:

public boolean remove(Object o) {
 if (o == null) {
 for (int index = 0; index < size; index++)
 if (elementData[index] == null) {
 fastRemove(index);
 return true;
 }
 } else {
 for (int index = 0; index < size; index++)
 if (o.equals(elementData[index])) {
 // 示例中调用的是此处的 fastRemove
 fastRemove(index);
 return true;
 }
 }
 return false;
}

而使 modCount 的值改变的是其中的 fastRemove 方法。

fastRemove 源码如下:

private void fastRemove(int index) {
 // 此处 modCount + 1
 modCount++;
 int numMoved = size - index - 1;
 if (numMoved > 0)
 System.arraycopy(elementData, index+1, elementData, index,
 numMoved);
 elementData[--size] = null; // clear to let GC do its work
}

而真正使 expectedModCount = modCount 执行的源码如下:

public void remove() {
 if (lastRet < 0)
 throw new IllegalStateException();
 checkForComodification();
 try {
 AbstractList.this.remove(lastRet);
 if (lastRet < cursor)
 cursor--;
 lastRet = -1;
 expectedModCount = modCount;
 } catch (IndexOutOfBoundsException e) {
 throw new ConcurrentModificationException();
 }
}

此代码在内部类 Itr 中。

这也就是为什么会说,如果 list 在循环中有删除操作,最好用 iterator 迭代的方式去做。

四、总结

简单总结一下

  • list.remove() 没有对 expectedModCount 重新赋值
  • iterator.remove()expectedModCount 重新赋值

建议大家跟踪一下源代码,代码量不多,也很容易理解。

arraylist 循环删除

rivate class Itr implements Iterator<E> {
 /**
 * Index of element to be returned by subsequent call to next.
 */
 int cursor = 0;
 /**
 * Index of element returned by most recent call to next or
 * previous. Reset to -1 if this element is deleted by a call
 * to remove.
 */
 int lastRet = -1;
 /**
 * The modCount value that the iterator believes that the backing
 * List should have. If this expectation is violated, the iterator
 * has detected concurrent modification.
 */
 int expectedModCount = modCount;
 public boolean hasNext() {
 return cursor != size();
 }
 public E next() {
 checkForComodification();
 try {
 int i = cursor;
 E next = get(i);
 lastRet = i;
 cursor = i + 1;
 return next;
 } catch (IndexOutOfBoundsException e) {
 checkForComodification();
 throw new NoSuchElementException();
 }
 }
 public void remove() {
 if (lastRet < 0)
 throw new IllegalStateException();
 checkForComodification();
 try {
 AbstractList.this.remove(lastRet);
 if (lastRet < cursor)
 cursor--;
 lastRet = -1;
 expectedModCount = modCount;
 } catch (IndexOutOfBoundsException e) {
 throw new ConcurrentModificationException();
 }
 }
 final void checkForComodification() {
 if (modCount != expectedModCount)
 throw new ConcurrentModificationException();
 }
}

作者:饭娱咖啡

本文为云栖社区内容,未经允许不得转载。

激活谷谷主为您准备了激活教程,为节约您的时间请移步至置顶文章:https://sigusoft.com/99576.html

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。 文章由激活谷谷主-小谷整理,转载请注明出处:https://sigusoft.com/15709.html

(0)
上一篇 2024年 5月 19日
下一篇 2024年 5月 19日

相关推荐

  • Goland激活2024.1.2(JetBrains GoLand2024.1.1)

    Goland激活2024.1.2(JetBrains GoLand2024.1.1)

    激活谷笔记 2024年 6月 7日
  • 半导体存储器的分类_半导体存储器的分类和特点

    半导体存储器的分类_半导体存储器的分类和特点计算机存储系统简介存储器系统是 不同容量,成本和访问时间的存储设备的层次结构。 包括最快的寄存器,高速缓存存储器和主存储器以及磁盘等。 随机访问存储器: 包括静态存储器和动态存储器(静态RAM

    2024年 5月 21日
  • 二叉搜索树后序遍历_二叉搜索树后序遍历的特点

    二叉搜索树后序遍历_二叉搜索树后序遍历的特点二叉搜索树 —— 不能说完全没用,至少思想是经典的前言我们知道,「 顺序表 」 可以 「 快速索引 」 数据,而 「 链表 」 则可以快速的进行数据的「 插入 和 删除 」。那么,有没有一种数据结构

    2024年 5月 29日
  • IDEA激活2024.1.1(PyCharm 2024.1.2 最新激活码,激活成功教程版安装教程(亲测有效~))

    IDEA激活2024.1.1(PyCharm 2024.1.2 最新激活码,激活成功教程版安装教程(亲测有效~))

    2024年 6月 7日
  • word文档怎么做目录链接_已经写好的word怎么加目录

    word文档怎么做目录链接_已经写好的word怎么加目录怎么建立关键词词库?建立关键词词库要分两部分:  1、已实现关键词排名词库;  2、计划实现关键词词库。  我们要做的就是建立计划实现关键词词库。我们知道自己的行业,知道自己行业的分类,所涉及到的那些大词,看百度指数,确定词根,来看看这个词够不够大。  确定词根的方法还有一种,就是一级原

    2024年 5月 14日
  • html+css网页设计软件_htmlcss网页设计软件

    html+css网页设计软件_htmlcss网页设计软件HTML+CSS网页设计随着互联网的普及,越来越多的企业和个人开始意识到拥有一个专业的网站对于品牌形象和业务发展的重要性。而HTML+CSS则是快速搭建网站的有效工具。本文将介绍HTML+CSS网页设计的基础知识、步骤和技巧,帮助大家快速

    2024年 5月 24日
  • 新闻管理系统设计内容包括_新闻管理系统设计内容包括哪些

    新闻管理系统设计内容包括_新闻管理系统设计内容包括哪些《在线新闻管理系统》数据库设计完整的数据库从设计到实现的完整流程:1.需求分析2.概念模型3.逻辑模型4.物理模型1.需求分析系统总需求:系统建设目标是建设一个集自动化、信息化、网络化、全球化为一体的,符合新闻企业管理特点的在线新闻管理系

    2024年 5月 25日
  • 三维全景技术的概念是什么_科学与技术的关系是什么概念

    三维全景技术的概念是什么_科学与技术的关系是什么概念元宇宙产业进化论:共识能量场与元系经济系统近几月,以ChatGPT为代表的生成式AI霸占科技圈C位。相比之下,元宇宙、区块链、Web3、VR等热门词的讨论度似乎落了下风。事实上,政府层面、学术圈、技术圈、应用层对元宇宙的探讨与探索从未停止。如果话题降温代表的是一个

    2024年 5月 15日
  • 7zip压缩完文件找不到了_压缩文件修改后保存找不到了

    7zip压缩完文件找不到了_压缩文件修改后保存找不到了关于解压软件7zip的用法介绍软件下载地址:https://pan.xunlei.com/s/VNn8kVmiFOS_WhBc_ZmAuthKA1?pwd=zi7m#电脑版解压方法:第一步:下载7zip软件请使用7zip进行解压,其它压缩软件解压可能出现各种错误。解压码统一为(

    2024年 5月 13日
  • 指针数组在函数中的应用_指针数组在函数中的应用实例

    指针数组在函数中的应用_指针数组在函数中的应用实例函数指针数组一、概念函数指针: 一个指向函数的指针。一般用函数名表示。函数指针数组:元素为函数指针的数组。转移表。c语言中函数不可以定义为数组,只能通过定义函数指针来操作。二、函数指针数组的使用例子#include <stdio.h>#include <std

    激活谷笔记 2024年 5月 20日
  • 哈夫曼树的构造例题及解析答案_哈夫曼树的构造例题及解析答案

    哈夫曼树的构造例题及解析答案_哈夫曼树的构造例题及解析答案哈夫曼树的构造和哈夫曼编码实现详细讲解(含例题详细讲解)以下是构造哈夫曼树并根据哈夫曼树构造哈夫曼编码的步骤和示例代码:1. 统计每个字符出现的频率,并将它们存储在一个字典中。2. 将每个字符及其频率作为一个节点,构造一个森林。3. 从森林中选

    激活谷笔记 2024年 5月 22日
  • eclipse运行不了怎么办_eclipse运行一直显示一个结果

    eclipse运行不了怎么办_eclipse运行一直显示一个结果72道Java线程面试题,一题一答案,不搞花里胡哨1) 什么是线程? 线程是操作系统能够进行运算调度的最小单位,它被包含在进程之中,是进程中的实际运作单位。程序员可以通过它进行多处理器编程,你可以使用多线程对运算密集型任务提速。比如,如果一个线程完成一个任务要100毫秒,那么用十个线程完成该

    2024年 5月 12日
关注微信