从Java中从ArrayList中删除元素时常见的问题之一是ConcurrentModificationException。 如果您对索引使用经典的for循环或增强的for循环,并尝试使用remove()方法从ArrayList中remove()元素,则将获得C oncurrentModificationException但如果使用Iterator的remove方法或ListIterator的
remove()方法,那么您将不会遇到此错误,并且能够删除该元素。 这是Java中的一条不成文的规则,在遍历列表时,在集合支持故障安全迭代器(例如CopyOnWriteArrayList remove()之前,不应该add()remove()元素,该迭代器对列表的副本而不是原始列表进行操作。

出现此错误的主要问题是,它使开发人员混淆了列表正在被多个线程修改,这就是Java抛出此错误的原因,这不是事实。 大多数时候
即使没有多个线程修改列表,也会出现ConcurrentModificationException

这是用词不当,请不要为此而上当。 尽管似乎自然而然地认为其他线程可能试图同时修改集合,但这通常违反了Java规则。

在本文中,我将解释此错误,并且我们将提供许多代码示例,即使使用单线程也可以重现此代码,并了解如何在Java中修改ArrayList时如何避免并发修改错误。

顺便说一句,如果您不熟悉集合类(例如ArrayList本身),那么您应该参加在线课程,例如
Java基础知识:学习在Udemy上编写正确的代码是一个不错的起点。

单线程中的ConcurrentModificationException

这是在Java中再现并发修改异常的第一个示例。 在此程序中,我们使用增强的foreach循环遍历ArrayList并删除选择性元素,例如,使用ArrayList的remove方法删除匹配特定条件的元素。

例如,在下面的代码中,我们首先添加了几本不错的编程书,例如, Programming Pearls , Clean Code , Code Complete到ArrayList中,然后删除标题中带有Code的任何元素。

package beginner;import java.util.ArrayList;
import java.util.List;public class HelloWorldApp{public static void main(String... args){List<String> listOfBooks = new ArrayList<>();  listOfBooks.add("Programming Pearls");listOfBooks.add("Clean Code");listOfBooks.add("Effective Java");listOfBooks.add("Code Complete");// Using forEach loop to iterate and removing // element during iteration will throw // ConcurrentModificationException in Javafor(String book: listOfBooks){if(book.contains("Code"));{listOfBooks.remove(book);}}}}
Output
Exception in thread "main" java.util.ConcurrentModificationExceptionat java.util.ArrayList$Itr.checkForComodification(Unknown Source)at java.util.ArrayList$Itr.next(Unknown Source)at beginner.HelloWorldApp.main(HelloWorldApp.java:18)

您可以看到即使我们只有一个线程,即与ArrayList一起运行的主线程,也会出现此错误。 之所以会出现ConcurrentModification错误,是因为我们没有使用Iterator,而是仅调用listOfBooks.remove()方法。

在这段代码中,我使用的Java 1.5增强的for循环,你必须知道如何for循环增强 Java中的作品。

for循环和增强的for循环之间的区别在于,以后内部使用Iterator遍历集合的所有元素。 有关更深入的讨论,请参见此处

使用Classical for循环和ArrayList.remove(index)

这是从ArrayList中删除元素的另一个有趣的代码示例。 令人惊讶的是,当您第一次运行此代码时,它不会引发ConcurrentModificationException吗? 你知道为什么吗?

好吧,在查看代码后的解释之前,请尝试一下。 确实是有关Java编程语言和Collection框架的此类次要细节,这将使您成为一名优秀的开发人员,并且如果您正在为此做好准备,还将帮助您获得Java认证 。

package beginner;import java.util.ArrayList;
import java.util.List;public class HelloWorldApp{public static void main(String... args){List<String> listOfBooks = new ArrayList<>();  listOfBooks.add("Programming Pearls");listOfBooks.add("Clean Code");listOfBooks.add("Effective Java");listOfBooks.add("Code Complete");System.out.println("List before : " + listOfBooks);for(int i=0; i<listOfBooks.size(); i++){String book = listOfBooks.get(i);if(book.contains("Programming")){System.out.println("Removing " + book);listOfBooks.remove(i); // will throw CME}}System.out.println("List after : " + listOfBooks);}}Output
List before : [Programming Pearls, Clean Code, Effective Java, Code Complete]
Removing Programming Pearls
List after : [Clean Code, Effective Java, Code Complete]

这段代码不会引发ConcurrentModificationException因为在这里我们没有使用 Iterator,而是在使用传统的for循环。

是Iterator引发ConcurrentModificationException ,而不是ArrayList的remove方法,因此在下面的代码中看不到该错误。

如果查看ArrayList.java的代码,您会注意到有一个实现Iterator接口的嵌套类,并且next()方法调用checkForComodification()函数,该函数实际上检查ArrayList在迭代过程中是否已修改,如果modCount没有, “T配以expectedModCount则抛出ConcurrentModificationException

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

此类问题在Oracle Java认证中也很受欢迎,例如OCAJP( 1z0-808 )和OCPJP( 1Z0-809 ),因此,如果您准备参加这些考试,则应该知道答案。

这是ArrayList.java类的完整代码片段,供您快速参考:

使用迭代器,但使用ArrayList的remove方法

现在,让我们看另一个代码示例,其中Java程序员认为他已正确完成了所有工作,但仍收到并发修改异常。 您能发现错误吗? 这确实很常见,我在Java论坛,StackOverflow和要求解决此问题的Facebook Java组上经常看到这种代码。

package beginner;import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;public class HelloWorldApp{public static void main(String... args){List<String> listOfBooks = new ArrayList<>();  listOfBooks.add("Programming Pearls");listOfBooks.add("Clean Code");listOfBooks.add("Effective Java");listOfBooks.add("Code Complete");Iterator<String> iterator = listOfBooks.iterator();while(iterator.hasNext()){String book = iterator.next();listOfBooks.remove(book);}}}Output
Exception in thread "main" java.util.ConcurrentModificationExceptionat java.util.ArrayList$Itr.checkForComodification(Unknown Source)at java.util.ArrayList$Itr.next(Unknown Source)at beginner.HelloWorldApp.main(HelloWorldApp.java:18)

这段代码的真正问题在于,即使代码使用Iterator遍历ArrayList ,也并不是真正使用Iterator.remove()方法删除该元素。 它只是使用Iterator获取下一个元素,但调用ArrayList.remove()方法删除该元素。

我知道,当您知道原因时就很容易了,但实际上,很多时候程序员甚至要花几个小时才能弄清楚问题出在哪里。 因此,请当心。

顺便说一句,如果您正在学习Java,那么我建议加入Complete Java Masterclass,以更好地学习Java并避免此类常见错误。

删除元素的正确方法是使用Iterator的remove方法

最后,这是在迭代过程中从ArrayList删除元素的正确方法。 在此示例中,我们使用Iterator进行了迭代以及删除了元素。 该代码还可以,但是有一个严重的限制,您只能使用此代码删除当前元素。 您不能从Java中的ArrayList中删除任何任意元素。

package beginner;import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;public class HelloWorldApp{public static void main(String... args){List<String> listOfBooks = new ArrayList<>();  listOfBooks.add("Programming Pearls");listOfBooks.add("Clean Code");listOfBooks.add("Effective Java");listOfBooks.add("Code Complete");System.out.println("List before : " + listOfBooks);Iterator<String> iterator = listOfBooks.iterator();while(iterator.hasNext()){String book = iterator.next();System.out.println("Removing " + book);iterator.remove();}System.out.println("List after : " + listOfBooks);}}
Output
List before : [Programming Pearls, Clean Code, Effective Java, Code Complete]
Removing Programming Pearls
Removing Clean Code
Removing Effective Java
Removing Code Complete
List after : []

同样的行为也适用于ListIterator 。 我的意思是您可以用ListIterator替换Iterator,并且代码可以正常工作。 ListIterator还允许您在两个方向上进行导航,即向前和向后导航。

这就是在迭代过程中如何从ArrayList中删除元素时如何避免ConcurrentModificationException的全部内容。 您可以使用相同的技术来避免ConcurrentModificationException同时从具有快速失败的Iterator的任何其他集合类中删除元素,例如LinkedList。 顺便说一句,如果您不熟悉Java编程,那么可以参加一门很好的综合课程,例如Java Basics:学习在Udemy上编写正确的方法 ,可以帮助您更好更快地学习Java。

您可能喜欢的其他Java故障排除指南

如何在Java中解决ArrayIndexOutOfBoundsException? ( 指南 )
如何在Java中解决NullPointerException? (指南)
如何解决“系统找不到指定的路径”错误? ( 解决方案 ) 从命令行运行Java程序时如何解决NoClassDefFoundError? ( 解决方案 ) 如何解决Android Studio中的“找不到JVM,请安装64位JDK”错误? ( 解决方案 ) 如何处理JDBC和MySQL中SQLException“找不到合适的驱动程序”错误? ( 指南 ) 如何解决Java中的NumberFormatException? ( 指南 ) 如何解决Minecraft – java.lang.UnsatisfiedLinkError:lwjgl64.dll:访问被拒绝? ( 解决方案 ) 如何在Java中修复java.lang.ArrayIndexOutOfBoundsException:1? ( 解决方案 ) 如何修复java.net.SocketException:软件导致连接中止:recv失败( 修复 )

感谢您阅读本教程,如果您喜欢本教程,请与您的朋友和同事分享。 如果您有任何问题或建议,请发表评论。

翻译自: https://www.javacodegeeks.com/2018/01/deal-concurrentmodificationexception-java-beware-removing-elements-arraylist-loop.html

如何在Java中处理ConcurrentModificationException? 在循环中从ArrayList中删除元素时要当心...相关推荐

  1. 第三次学JAVA再学不好就吃翔(part81)--去除ArrayList中重复元素

    学习笔记,仅供参考 因为我突然懒了,所以这个Blog以代码为主,解释为辅 文章目录 集合 去除ArrayList中重复的字符串元素 去除ArrayList中重复的自定义对象元素 LinkedList的 ...

  2. Java中List、Map、Set三个接口,存取元素时,各有什么特点?

    特点 List与Set都是单列元素的集合,它们有一个功共同的父接口Collection. 1.Set里面不允许有重复的元素, 存元素:add方法有一个boolean的返回值,当集合中没有某个元素,此时 ...

  3. c语言中的下标变量是什么,c语言引用数组元素时其数组下标的允许的数据类型是什么...

    c语言引用数组元素时其数组下标的允许的数据类型是什么 发布时间:2020-07-30 11:56:52 来源:亿速云 阅读:621 作者:Leah c语言引用数组元素时其数组下标的允许的数据类型是什么 ...

  4. python中del和pop有什么区别_Python列表删除元素del、pop()和remove()的区别小结

    前言 在python列表的元素删除操作中, del, pop(), remove()很容易混淆, 下面对三个语句/方法作出解释 del语句 del语句可以删除任何位置处的列表元素, 若知道某元素在列表 ...

  5. java 中的reader_java-无限循环中的ItemReader reader()

    我用JdbcTemplate实现了ItemReader. 问题在于read()在无限循环中被调用. public class MyReader implements ItemReader , Init ...

  6. java二重循环continue_双重循环中,内循环中的continue语句的作用是结束内循环,并继续执行外循环。? 正确|错误...

    [判断题]银行已代企业收款入账,而企业尚未收到银行的收款通知还未入账,造成企业存款余额大于银行对账单余额. 下列选项中,属于餐饮企业前台服务工作岗位的是: [填空题]在Java中强制类型转换分为 和 ...

  7. java如何把文件中的内容存到一个动态数组arraylist中_如何动态地向Java中的数组添加项目?...

    由于数组的大小是固定的,因此您不能动态地向其中添加元素.但是,如果您仍然想要这样做,将数组转换为ArrayList对象. 将所需元素添加到数组列表. 将数组列表转换为数组. 示例import java ...

  8. php 循环中return,php中for循环遇上return的示例代码分享

    先看下以下方法的打印结果以及返回值:public static void main(String[] args) { System.out.println("返回值:" + tes ...

  9. java数组末尾添加元素_JavaScript 数组 Array对象增加和删除 元素

    pop 方法 移除数组中的最后一个元素并返回该元素. arrayObj.pop( ) 必选的 arrayObj 引用是一个 Array 对象. 说明 如果该数组为空,那么将返回 undefined. ...

最新文章

  1. virtual server2005下创建citrix集群的一点记录
  2. JAVA struts2
  3. Linux复习资料——MySQL-client-5.6.50-1.el7.x86_64与MySQL-server-5.6.50-1.el7.x86_64包安装MySQL全过程
  4. IT巨头埃森哲遭 LockBit 勒索攻击,黑客威胁泄露数据
  5. docker 安装 MySQL 8,并减少内存占用 记录
  6. python和c语言的区别-python和c语言的区别是什么
  7. JAVA输入jdb,解决 JAVA 单步调试键盘输入被 JDB 占用的问题
  8. linux 下好用的音乐播放器介绍(转载)
  9. 常见十大漏洞总结(原理、危害、防御)
  10. NFT 的价值从何而来
  11. 智能网卡的网络加速技术
  12. 计算机英语中poke什么意思,poke one是什么意思
  13. Greenbone GSM Community Edition设置
  14. 【shell】远程执行shell|多节点并行执行shell|远程执行注意
  15. 图建模的 Schema 对应的 NebulaGraph DDL
  16. 薄荷英语---《心理学》20180823
  17. rpa打开浏览器_《基于RPA的财务机器人实务》课堂笔记请您签收
  18. 1166 Summit
  19. 达梦数据库FINDS_IN_SET函数
  20. oracle 1899年异常,BootStrap时间选择框架出现1899解决办法

热门文章

  1. 对弈(nim-k游戏博弈)
  2. P6222-「P6156 简单题」加强版【莫比乌斯反演】
  3. ATcoder-Replace Digits【线段树】
  4. P3369-[模板]普通平衡树【Splay】
  5. P1032-字串变换【bfs】
  6. ssl2345-繁忙的都市
  7. 【DP】小明在边塞(jzoj 2147)
  8. ACM-ICPC 2018 徐州赛区网络预赛 D. EasyMath
  9. 如何设计一个高可用的运营系统
  10. 避免代码冗余,使用接口和泛型重构Java代码