如何在Java中处理ConcurrentModificationException? 在循环中从ArrayList中删除元素时要当心...
从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中删除元素时要当心...相关推荐
- 第三次学JAVA再学不好就吃翔(part81)--去除ArrayList中重复元素
学习笔记,仅供参考 因为我突然懒了,所以这个Blog以代码为主,解释为辅 文章目录 集合 去除ArrayList中重复的字符串元素 去除ArrayList中重复的自定义对象元素 LinkedList的 ...
- Java中List、Map、Set三个接口,存取元素时,各有什么特点?
特点 List与Set都是单列元素的集合,它们有一个功共同的父接口Collection. 1.Set里面不允许有重复的元素, 存元素:add方法有一个boolean的返回值,当集合中没有某个元素,此时 ...
- c语言中的下标变量是什么,c语言引用数组元素时其数组下标的允许的数据类型是什么...
c语言引用数组元素时其数组下标的允许的数据类型是什么 发布时间:2020-07-30 11:56:52 来源:亿速云 阅读:621 作者:Leah c语言引用数组元素时其数组下标的允许的数据类型是什么 ...
- python中del和pop有什么区别_Python列表删除元素del、pop()和remove()的区别小结
前言 在python列表的元素删除操作中, del, pop(), remove()很容易混淆, 下面对三个语句/方法作出解释 del语句 del语句可以删除任何位置处的列表元素, 若知道某元素在列表 ...
- java 中的reader_java-无限循环中的ItemReader reader()
我用JdbcTemplate实现了ItemReader. 问题在于read()在无限循环中被调用. public class MyReader implements ItemReader , Init ...
- java二重循环continue_双重循环中,内循环中的continue语句的作用是结束内循环,并继续执行外循环。? 正确|错误...
[判断题]银行已代企业收款入账,而企业尚未收到银行的收款通知还未入账,造成企业存款余额大于银行对账单余额. 下列选项中,属于餐饮企业前台服务工作岗位的是: [填空题]在Java中强制类型转换分为 和 ...
- java如何把文件中的内容存到一个动态数组arraylist中_如何动态地向Java中的数组添加项目?...
由于数组的大小是固定的,因此您不能动态地向其中添加元素.但是,如果您仍然想要这样做,将数组转换为ArrayList对象. 将所需元素添加到数组列表. 将数组列表转换为数组. 示例import java ...
- php 循环中return,php中for循环遇上return的示例代码分享
先看下以下方法的打印结果以及返回值:public static void main(String[] args) { System.out.println("返回值:" + tes ...
- java数组末尾添加元素_JavaScript 数组 Array对象增加和删除 元素
pop 方法 移除数组中的最后一个元素并返回该元素. arrayObj.pop( ) 必选的 arrayObj 引用是一个 Array 对象. 说明 如果该数组为空,那么将返回 undefined. ...
最新文章
- virtual server2005下创建citrix集群的一点记录
- JAVA struts2
- Linux复习资料——MySQL-client-5.6.50-1.el7.x86_64与MySQL-server-5.6.50-1.el7.x86_64包安装MySQL全过程
- IT巨头埃森哲遭 LockBit 勒索攻击,黑客威胁泄露数据
- docker 安装 MySQL 8,并减少内存占用 记录
- python和c语言的区别-python和c语言的区别是什么
- JAVA输入jdb,解决 JAVA 单步调试键盘输入被 JDB 占用的问题
- linux 下好用的音乐播放器介绍(转载)
- 常见十大漏洞总结(原理、危害、防御)
- NFT 的价值从何而来
- 智能网卡的网络加速技术
- 计算机英语中poke什么意思,poke one是什么意思
- Greenbone GSM Community Edition设置
- 【shell】远程执行shell|多节点并行执行shell|远程执行注意
- 图建模的 Schema 对应的 NebulaGraph DDL
- 薄荷英语---《心理学》20180823
- rpa打开浏览器_《基于RPA的财务机器人实务》课堂笔记请您签收
- 1166 Summit
- 达梦数据库FINDS_IN_SET函数
- oracle 1899年异常,BootStrap时间选择框架出现1899解决办法