百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 编程字典 > 正文

为什么循环删除List元素会报错(循环删除list中元素)

toyiye 2024-07-15 01:22 6 浏览 0 评论

描述

大家在工作中应该都会遇到从List集合中删除某一个或多个元素的业务场景

相信大家都会避开在循环里面删除元素,使用其他方式处理

很多面试官也都会问为什么循环里面不能删除元素?

示例

JAVA

复制代码

public static void main(String[] args) { List<String> list = new ArrayList<>(); list.add("test0"); list.add("test1"); list.add("test2"); list.add("test3"); list.add("test4"); list.add("test5"); System.out.println(list); list.remove(3); System.out.println(list); list.remove("test1"); System.out.println(list); for (int i = 0; i < list.size(); i++) { if (i == 2) { list.remove(i); } } System.out.println(list); Iterator<String> it = list.iterator(); while (it.hasNext()) { if (it.next().equals("test2")) { it.remove(); } } System.out.println(list); for (String s : list) { if ("test5".equals(s)) { list.remove(s); } } System.out.println(list); } 打印结果 [test0, test1, test2, test3, test4, test5] [test0, test1, test2, test4, test5] [test0, test2, test4, test5] [test0, test2, test5] [test0, test5] Exception in thread "main" java.util.ConcurrentModificationException at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:909) at java.util.ArrayList$Itr.next(ArrayList.java:859) at com.fc.store.Test.main(Test.java:46)

从打印结果可以看到

  1. 根据索引删除 --正常
  2. 根据元素删除 --正常
  3. 循环根据索引删除 --正常
  4. 迭代删除原始 --正常
  5. 循环根据元素删除 --不正常

执行过程

JAVA

复制代码

List<String> list = new ArrayList<>(); 集合初始化时 transient Object[] elementData; 空数组 private int size; 长度为0 protected transient int modCount = 0; 修改次数为0 添加元素 list.add("test0"); elementData[0] = "test0"; size = 1; modCount = 1; list.add("test1"); elementData[0] = "test0"; elementData[1] = "test1"; size = 2; modCount = 2; list.add("test2"); elementData[0] = "test0"; elementData[1] = "test1"; elementData[2] = "test2"; size = 3; modCount = 3; list.add("test3"); elementData[0] = "test0"; elementData[1] = "test1"; elementData[2] = "test2"; elementData[3] = "test3"; size = 4; modCount = 4; list.add("test4"); elementData[0] = "test0"; elementData[1] = "test1"; elementData[2] = "test2"; elementData[3] = "test3"; elementData[4] = "test4"; size = 5; modCount = 5; list.add("test5"); elementData[0] = "test0"; elementData[1] = "test1"; elementData[2] = "test2"; elementData[3] = "test3"; elementData[4] = "test4"; elementData[5] = "test5"; size = 6; modCount = 6; 可以发现每添加一个元素,集合size会增加1, 修改次数会增加1 根据索引删除 list.remove(3); elementData[0] = "test0"; elementData[1] = "test1"; elementData[2] = "test2"; elementData[4] = "test4"; elementData[5] = "test5"; size = 5; modCount = 7; 根据元素删除 list.remove("test1"); elementData[0] = "test0"; elementData[1] = "test2"; elementData[2] = "test4"; elementData[5] = "test5"; size = 4; modCount = 8; 循环根据索引删除 for (int i = 0; i < list.size(); i++) { if (i == 2) { list.remove(i); } } elementData[0] = "test0"; elementData[1] = "test2"; elementData[5] = "test5"; size = 3; modCount = 9; 循环根据索引删除 Iterator<String> it = list.iterator(); while (it.hasNext()) { if (it.next().equals("test2")) { it.remove(); } } elementData[0] = "test0"; elementData[5] = "test5"; size = 2; modCount = 10; 可以发现每删除一个元素后,集合size会减1,修改次数会增加1 循环根据元素删除 for (String s : list) { if ("test2".equals(s)) { list.remove(s); } } 就抛异常了,因为Iterator的next方法会校验是否修改 此时:expectedModCount = 10, modCount = 11 public E next() { checkForComodification(); } final void checkForComodification() { if (modCount != expectedModCount) { throw new ConcurrentModificationException(); } }

源码解析

List 的add和remove方法每次操作都会对modCount++

JAVA

复制代码

public boolean add(E e) { ensureCapacityInternal(size + 1); // Increments modCount!! elementData[size++] = e; return true; } public E remove(int index) { rangeCheck(index); modCount++; E oldValue = elementData(index); 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 return oldValue; } 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(index); return true; } } } return false; } private void fastRemove(int index) { 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 } private void ensureCapacityInternal(int minCapacity) { ensureExplicitCapacity(calculateCapacity(elementData, minCapacity)); } private void ensureExplicitCapacity(int minCapacity) { modCount++; }

Iterator 会继承List的modCount,并赋值给expectedModCount

JAVA

复制代码

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

Iterator 的next方法会校验modCount和expectedModCount 是否相同

JAVA

复制代码

public E next() { checkForComodification(); int i = cursor; if (i >= size) throw new NoSuchElementException(); Object[] elementData = ArrayList.this.elementData; if (i >= elementData.length) throw new ConcurrentModificationException(); cursor = i + 1; return (E) elementData[lastRet = i]; } final void checkForComodification() { if (modCount != expectedModCount) throw new ConcurrentModificationException(); }

由于在Iterator的便利中使用了List的remove方法,导致modCount增加了 所以在下次next方法中判断modCount和expectedModCount不一致就直接抛出了异常

解决方案

第一种使用迭代器删除

JAVA

复制代码

Iterator<String> it = list.iterator(); while (it.hasNext()) { if (it.next().equals("test2")) { it.remove(); } }

第二种for循环删除后要立即退出

JAVA

复制代码

for (String s : list) { if ("test5".equals(s)) { list.remove(s); return; } } JAVA8语法 list.removeIf("test5"::equals);

Set,Map 同理

同样Set,Map循环里面删除报错也是同样的原理

往往大家在遇到问题后,都只是找到了主要原因,但是并没有找到根本原因。

只有通过深入分析找到根本原因,制定预防措施(加入CR清单,添加扫码规则,制定奖惩措施),才能够真正避免问题

相关推荐

为何越来越多的编程语言使用JSON(为什么编程)

JSON是JavascriptObjectNotation的缩写,意思是Javascript对象表示法,是一种易于人类阅读和对编程友好的文本数据传递方法,是JavaScript语言规范定义的一个子...

何时在数据库中使用 JSON(数据库用json格式存储)

在本文中,您将了解何时应考虑将JSON数据类型添加到表中以及何时应避免使用它们。每天?分享?最新?软件?开发?,Devops,敏捷?,测试?以及?项目?管理?最新?,最热门?的?文章?,每天?花?...

MySQL 从零开始:05 数据类型(mysql数据类型有哪些,并举例)

前面的讲解中已经接触到了表的创建,表的创建是对字段的声明,比如:上述语句声明了字段的名称、类型、所占空间、默认值和是否可以为空等信息。其中的int、varchar、char和decimal都...

JSON对象花样进阶(json格式对象)

一、引言在现代Web开发中,JSON(JavaScriptObjectNotation)已经成为数据交换的标准格式。无论是从前端向后端发送数据,还是从后端接收数据,JSON都是不可或缺的一部分。...

深入理解 JSON 和 Form-data(json和formdata提交区别)

在讨论现代网络开发与API设计的语境下,理解客户端和服务器间如何有效且可靠地交换数据变得尤为关键。这里,特别值得关注的是两种主流数据格式:...

JSON 语法(json 语法 priority)

JSON语法是JavaScript语法的子集。JSON语法规则JSON语法是JavaScript对象表示法语法的子集。数据在名称/值对中数据由逗号分隔花括号保存对象方括号保存数组JS...

JSON语法详解(json的语法规则)

JSON语法规则JSON语法是JavaScript对象表示法语法的子集。数据在名称/值对中数据由逗号分隔大括号保存对象中括号保存数组注意:json的key是字符串,且必须是双引号,不能是单引号...

MySQL JSON数据类型操作(mysql的json)

概述mysql自5.7.8版本开始,就支持了json结构的数据存储和查询,这表明了mysql也在不断的学习和增加nosql数据库的有点。但mysql毕竟是关系型数据库,在处理json这种非结构化的数据...

JSON的数据模式(json数据格式示例)

像XML模式一样,JSON数据格式也有Schema,这是一个基于JSON格式的规范。JSON模式也以JSON格式编写。它用于验证JSON数据。JSON模式示例以下代码显示了基本的JSON模式。{"...

前端学习——JSON格式详解(后端json格式)

JSON(JavaScriptObjectNotation)是一种轻量级的数据交换格式。易于人阅读和编写。同时也易于机器解析和生成。它基于JavaScriptProgrammingLa...

什么是 JSON:详解 JSON 及其优势(什么叫json)

现在程序员还有谁不知道JSON吗?无论对于前端还是后端,JSON都是一种常见的数据格式。那么JSON到底是什么呢?JSON的定义...

PostgreSQL JSON 类型:处理结构化数据

PostgreSQL提供JSON类型,以存储结构化数据。JSON是一种开放的数据格式,可用于存储各种类型的值。什么是JSON类型?JSON类型表示JSON(JavaScriptO...

JavaScript:JSON、三种包装类(javascript 包)

JOSN:我们希望可以将一个对象在不同的语言中进行传递,以达到通信的目的,最佳方式就是将一个对象转换为字符串的形式JSON(JavaScriptObjectNotation)-JS的对象表示法...

Python数据分析 只要1分钟 教你玩转JSON 全程干货

Json简介:Json,全名JavaScriptObjectNotation,JSON(JavaScriptObjectNotation(记号、标记))是一种轻量级的数据交换格式。它基于J...

比较一下JSON与XML两种数据格式?(json和xml哪个好)

JSON(JavaScriptObjectNotation)和XML(eXtensibleMarkupLanguage)是在日常开发中比较常用的两种数据格式,它们主要的作用就是用来进行数据的传...

取消回复欢迎 发表评论:

请填写验证码