Java 迭代器删除元素ConcurrentModificationException异常。
Java是不支持容器类在使用迭代器迭代过程中,使用如 list.remove(obj)方法删除元素。否则会抛出ava.util.ConcurrentModificationException异常。应该使用iterator.remove()方法删除当前迭代到的元素。
这是因为Java集合中有一种叫fail-fast的机制,即如果多个线程对同一个集合的内容进行操作时,则会产生fail-fast事件,即抛出异常。
比如下面代码
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class Hello {
public static void main(String[] args) {
List<String> all=new ArrayList<String>();
all.add("Hello");
all.add("_");
all.add("World!!");
Iterator<String> iterator=all.iterator();
while(iterator.hasNext())
{
String str=iterator.next();
if("Hello".equals(str))
{
all.remove(str);
}
else
{
System.out.println( str+" ");
}
}
System.out.println("\n删除\"_\"之后的集合当中的数据为:"+all);
}
}
删除元素会抛出下面异常

这里在迭代过程中改变了原来集合的存储内容,因此出发了fail-fast机制。
fail-fast机制是这样设置的。以上面代码抛出的异常为例。查看ArrayList.类的源码,可以看到抛出异常的函数为

这里的modCount文档是这样解释的
The number of times this list has been structurally modified. Structural modifications are those that change the size of the list, or otherwise perturb it in such a fashion that iterations in progress may yield incorrect results.
大概意思就是list的结构被修改的次数。结构的修改包括改变list的大小,或者其他可能使迭代结果产生错误的干扰行为。
expectedModCount注释为:
The modCount value that the iterator believes that the backing List should have. If this expectation is violated, the iterator has detected concurrent modification.
因此若是在迭代过程中改变了List的结构,即使用了remove,或者等add方法则会导致expectedModCount的值与modCount的值不同,就会抛出异常。
但是有一个奇特的现象,如果我们使用List.remove(obj)方法删除倒数第二个元素则不会超出异常。如
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class Hello {
public static void main(String[] args) {
List<String> all=new ArrayList<String>();
all.add("Hello");
all.add("_");
all.add("World!!");
Iterator<String> iterator=all.iterator();
while(iterator.hasNext())
{
String str=iterator.next();
if("_".equals(str))
{
all.remove(str);
}
else
{
System.out.println( str+" ");
}
}
System.out.println("\n删除\"_\"之后的集合当中的数据为:"+all);
}
}
注意这里删除的'_'为集合中的倒数第二个元素。同时注意代码有,若没有删除此元素,则输出此元素,即else里面的内容。结果为

不是说,不能用List.remove(obj)的方式,只能用iterator.remove()的方式删除,这里为什么没有抛异常。原因是这样。
看ArrayList的hasNext()方法与next()方法的源码
size为当前集合的大小,cursor为下个元素的索引,初始化为0

其中checkForComodification的源码为,两个变量的的解释上面也已经说了。

我们的迭代器部分代码按下面步骤执行:
all里面存储了“Hello”,"_","world!"三个元素
- iterator.hasNext(),
- cursor=0,size=3。cursor!=size,返回true,进入while内
- String str=iterator.next(),
- checkForComodification(),此时modCount=0,expectedModCount=0,因此没有抛出异常,
- cursor=i+1,即cursor=1
- return elementData[lastRet=i],str='Hello'
- '_'.equals(str)为False,输出Hello
- iterator.hasNext(),
- cursor=1,size=3。cursor!=size,返回true,进入while内
- String str=iterator.next(),
- checkForComodification(),此时modCount=0,expectedModCount=0,因此没有抛出异常,
- cursor=i+1,即cursor=2
- return elementData[lastRet=i],str='_'
- '_'.equals(str)为Ture,
- all.remove(str)
- modCount=1,因为改变了List的结构,因此modCount+1。
- iterator.hasNext(),
- cursor=2,size=2。cursor!=size,返回False,退出
可以看到,在这里就已经推出while循环了。不会在执行checkForComodification()方法,判断List结构有没有改变。
结论:因此只要对倒数第二个元素执行remove()方法,虽然会改变集合结构,但是因为也同时改变了集合size,使size与cursor的大小一样,就会直接循环,不会报异常。
Java 迭代器删除元素ConcurrentModificationException异常。的更多相关文章
- 集合遍历过程iterator, 添加删除元素报异常
list set 遍历过程中添加或者删除元素,报异常. 使用iterator 也会报异常 ConcurrentModificationException remove只能用迭代器的remove,而 ...
- STL 中 使用迭代器删除元素的问题
在vector中删除,大家都知道,直接erase的话,这种写法很有问题.因为erase(iter)之后iter指针就变成野指针了,此时继续iter++就会出问题. for(auto iter = v. ...
- C++中利用迭代器删除元素会发生什么?
转自:https://blog.csdn.net/yf_li123/article/details/75003425#comments (1)对于关联容器(如map,set,multimap,mu ...
- 该文档举例说明了multimap的查找和删除元素的使用
该文档举例说明了multimap的查找和删除元素的使用. 其中,在使用迭代器遍历元素的时候,如果使用了删除迭代器的操作,那么需要小心迭代器失效的情况. /* 功能说明: multimap的查找和删除元 ...
- Java中集合删除元素时候关于ConcurrentModificationException的迷惑点
下面的示例来至于阿里巴巴Java开发手册的集合处理部分的第7条: 运行如下代码,会发现正确运行. public static void hasNotExcption() { List<Strin ...
- Java循环删除集合多个元素的正确打开方式
首先说下不正确的打开方式: 第一:使用for循环删除集合的元素,示例代码如下 ArrayList<String> list = new ArrayList<String>(Ar ...
- Java ConcurrentModificationException异常原因和解决方法
Java ConcurrentModificationException异常原因和解决方法 在前面一篇文章中提到,对Vector.ArrayList在迭代的时候如果同时对其进行修改就会抛出java.u ...
- Java并发编程:Java ConcurrentModificationException异常原因和解决方法
Java ConcurrentModificationException异常原因和解决方法 在前面一篇文章中提到,对Vector.ArrayList在迭代的时候如果同时对其进行修改就会抛出java.u ...
- Java HashMap 如何正确遍历并删除元素
(一)HashMap的遍历 HashMap的遍历主要有两种方式: 第一种采用的是foreach模式,适用于不需要修改HashMap内元素的遍历,只需要获取元素的键/值的情况. HashMap<K ...
随机推荐
- 阶段1 语言基础+高级_1-3-Java语言高级_04-集合_08 Map集合_2_Map常用子类
常用的实现类HashMap 它的子类.LinkedHaspMap
- 【openstf】自己的云测平台——mac安装openstf
openstf的github地址:https://github.com/openstf/stf 上图可以清晰看出openstf的使用场景和效果 openstf是一个web应用程序,用于远程调试智能 ...
- 【ABAP系列】SAP ABAP ALV中的TOP_OF_PAGE添加任意图标
公众号:SAP Technical 本文作者:matinal 原文出处:http://www.cnblogs.com/SAPmatinal/ 原文链接:[MM系列]SAP ABAP ALV中的TOP_ ...
- 【Unity Shader】---准确认识SubShader语义块结构、渲染状态设定、Tags标签
一[SubShader] 每个UnityShader文件可以包含多个SubShader语义块,但至少要有一个.当Unity需要加载这个UnityShader时,Unity会扫描所有的SubShader ...
- 一次特殊的“VARCHAR转numeric失败”错误记录
今天接触到一个很有意思的问题.当我在执行一条INSERT的sql语句时,他总是报字符串转数字类型失败. 问题 首先,该表中的所有数字类型的字段都是非必填,其次,每个数字类型的字段都有默认值.最令我感到 ...
- APM全链路监控--日志收集篇
一.监控的意义: 随着互联网普及的广度和深度,对于项目的监控显得格外重要:无论是web服务器进程.内存.cpu等资源监控,还是爬虫程序请求频率,状态码以及储存结果的监控,都需要一个及时的反馈机制. 二 ...
- 剑指Offer编程题(Java实现)——二维数组中的查找
题目描述 在一个二维数组中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序.请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数 ...
- EA逆向生成数据库E-R图(mysql数据库-->ER图)
[1]选择 工具-->ODBC-Data-Sources [2]ODBC数据源管理器 ,点击添加 [3]选择一个mysql驱动 ,点击MySQL ODBC 5.1 Driver(其它同理), ...
- VS Code 设置双快捷键(快速移动光标)
平时写代码会经常用到上下左右键,比如打出两个括号 () ,编辑完之后得按到右括号后面 难免有这样的场景需要在编辑代码的时候小范围地移动光标,笔者在别的ide的习惯是通过“alt + jkli”来实现光 ...
- JavaScript中Number(),parseInt()和parseFloat()区别
parseInt() 函数可解析一个字符串,并返回一个整数; parseFloat() 函数可解析一个字符串,并返回一个浮点数, 以上都是截取转换,具体代码如下: alert(parseInt(&qu ...