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!"三个元素

  1. iterator.hasNext(),

    1. cursor=0,size=3。cursor!=size,返回true,进入while内
  2. String str=iterator.next(),
    1. checkForComodification(),此时modCount=0,expectedModCount=0,因此没有抛出异常,
    2. cursor=i+1,即cursor=1
    3. return elementData[lastRet=i],str='Hello'
  3. '_'.equals(str)为False,输出Hello
  4. iterator.hasNext(),
    1. cursor=1,size=3。cursor!=size,返回true,进入while内
  5. String str=iterator.next(),
    1. checkForComodification(),此时modCount=0,expectedModCount=0,因此没有抛出异常,
    2. cursor=i+1,即cursor=2
    3. return elementData[lastRet=i],str='_'
  6. '_'.equals(str)为Ture,
    1. all.remove(str)
    2. modCount=1,因为改变了List的结构,因此modCount+1。
  7. iterator.hasNext(),
    1. cursor=2,size=2。cursor!=size,返回False,退出

可以看到,在这里就已经推出while循环了。不会在执行checkForComodification()方法,判断List结构有没有改变。

结论:因此只要对倒数第二个元素执行remove()方法,虽然会改变集合结构,但是因为也同时改变了集合size,使size与cursor的大小一样,就会直接循环,不会报异常。

Java 迭代器删除元素ConcurrentModificationException异常。的更多相关文章

  1. 集合遍历过程iterator, 添加删除元素报异常

    list  set  遍历过程中添加或者删除元素,报异常. 使用iterator 也会报异常 ConcurrentModificationException remove只能用迭代器的remove,而 ...

  2. STL 中 使用迭代器删除元素的问题

    在vector中删除,大家都知道,直接erase的话,这种写法很有问题.因为erase(iter)之后iter指针就变成野指针了,此时继续iter++就会出问题. for(auto iter = v. ...

  3. C++中利用迭代器删除元素会发生什么?

    转自:https://blog.csdn.net/yf_li123/article/details/75003425#comments   (1)对于关联容器(如map,set,multimap,mu ...

  4. 该文档举例说明了multimap的查找和删除元素的使用

    该文档举例说明了multimap的查找和删除元素的使用. 其中,在使用迭代器遍历元素的时候,如果使用了删除迭代器的操作,那么需要小心迭代器失效的情况. /* 功能说明: multimap的查找和删除元 ...

  5. Java中集合删除元素时候关于ConcurrentModificationException的迷惑点

    下面的示例来至于阿里巴巴Java开发手册的集合处理部分的第7条: 运行如下代码,会发现正确运行. public static void hasNotExcption() { List<Strin ...

  6. Java循环删除集合多个元素的正确打开方式

    首先说下不正确的打开方式: 第一:使用for循环删除集合的元素,示例代码如下 ArrayList<String> list = new ArrayList<String>(Ar ...

  7. Java ConcurrentModificationException异常原因和解决方法

    Java ConcurrentModificationException异常原因和解决方法 在前面一篇文章中提到,对Vector.ArrayList在迭代的时候如果同时对其进行修改就会抛出java.u ...

  8. Java并发编程:Java ConcurrentModificationException异常原因和解决方法

    Java ConcurrentModificationException异常原因和解决方法 在前面一篇文章中提到,对Vector.ArrayList在迭代的时候如果同时对其进行修改就会抛出java.u ...

  9. Java HashMap 如何正确遍历并删除元素

    (一)HashMap的遍历 HashMap的遍历主要有两种方式: 第一种采用的是foreach模式,适用于不需要修改HashMap内元素的遍历,只需要获取元素的键/值的情况. HashMap<K ...

随机推荐

  1. Maven install报错:MojoFailureException ,To see the full stack trace of the errors, re-run Maven with the -e switch.解决

    报错日志: SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".SLF4J: Defaulting to ...

  2. 阶段1 语言基础+高级_1-3-Java语言高级_06-File类与IO流_09 序列化流_6_练习_序列化集合

  3. loc() iloc() at() iat()函数

    1 四个函数都是用于dataframe的定位 []用于直接定位. loc()函数是用真实索引,iloc()函数是用索引序号. loc()函数切片是左闭右闭,iloc()函数切片是左闭右开. at(), ...

  4. python读写ini配置文件

    像邮箱等信息是可以写在配置文件里面的,python有一个配置模块ConfigParser,可以处理配置文件信息 目录 1.配置模块ConfigParser 2.基本应用 1.配置模块ConfigPar ...

  5. CSS3------box-shadow,即单边阴影效果设置

    box-shadow修改元素的阴影效果要方便得多,因为box-shadow可以修改六个参数,得到不同的效果.下面结合一些简单的案例来对box-shadow属性进行演示说明. 1.单边阴影效果 定义元素 ...

  6. New start-开始我的学习记录吧

    不知道从何说起,就从眼下的感想开始吧. 转行是一件不容易的事情! 今天是来北京学习Java的第41天.小测验了两次,一次51分,一次54分. 下午有学长过来分享了他的成长经历,感触很多.不是灌鸡汤,也 ...

  7. 洛谷 P2672 推销员(贪心,模拟)

    传送门 解题思路 第一种: 对于选i家,很显然,a值前i-1家的一定会选,所以只需要考虑最后一家的选法.要么是选择a值第i大的(就不管s了),要么选择剩下的中s最大的. 我们把每一家的情况(s和a)存 ...

  8. 一键生成APK

    傻瓜式的生成APK网址:https://www.appbsl.com/ 第一步 第二步 第三步 第四步

  9. Python 的 time 模块导入及其方法

    时间模块很重要,Python 程序能用很多方式处理日期和时间,转换日期格式是一个常见的功能,讲解一下Python 的 time 模块导入及其方法. 1,time 模块导入 import time; # ...

  10. EF6 MySQL错误之“Specified key was too long; max key length is 767 bytes”

    由于 MySQL Innodb 引擎表索引字段长度的限制为 767 字节,因此对于多字节字符集的大字段(或者多字段组合索引),创建索引会出现上面的错误. 以 utf8mb4 字符集 字符串类型字段为例 ...