Exception in thread "main" java.util.ConcurrentModificationException 并发修改异常引发的思考!

1 foreach循环删除元素

  ①list遍历删除元素时会报错,比如下面删除字符串"aa",也有遍历不报错的例子,看下面的例子

public class TestMain {
public static void main(String[] args) {
ArrayList<String> array = new ArrayList<String>();
array.add("cc");
array.add("aa");
array.add("bb");
array.add("aa");
for (String str : array) {
if("aa".equals(str)){
array.remove(str);
}
} System.out.println(array.size()); }
} console: java.util.ConcurrentModificationException

  ②下面删除字符串"aa"不会报错

public class TestMain {
public static void main(String[] args) {
ArrayList<String> array = new ArrayList<String>();
array.add("cc");
array.add("aa");
array.add("bb");
for (String str : array) {
if("aa".equals(str)){
array.remove(str);
}
} System.out.println(array.size()); }
} console : 2

提出问题:为什么上面都是遍历删除,第二个确没有报错呢?

结论:其实原因很简单,因为第二个例子没有走iterator的next方法,删除了字符串"aa"之后,执行hasNext方法返回false直接退出遍历了,hasNext中就是判断cursor != size;此时的cursor是2,而size正好也是2,所以退出了遍历。

而第一个例子删除字符串"aa"之后,cursor=2,size=3,所以hasNext方法返回的true,会执行next方法。

注:只要遍历中remove了,expectedModCount和modCount就不相等了。

注2:cursor 就类似游标,这里表示第几次,size就是元素个数

同理:iterator的形式遍历同foreach。

2 上面都是通过foreach的方式进行的,如果用普通for循环会怎么样

public class TestMain {
public static void main(String[] args) {
ArrayList<String> array = new ArrayList<String>();
array.add("cc");
array.add("aa");
array.add("bb");
array.add("aa");
for(int i = 0;i < array.size();i++){
if("aa".equals(array.get(i))){
array.remove(i);
}
}
System.out.println(array.size()); }
} console: 2

  结论: 普通for循环可以正常删除,他是根据索引进行删除的,所以没有影响。

根据报错信息可以看到是进入checkForComodification()方法的时候报错了,也就是说modCount != expectedModCount。具体的原因,是在于foreach方式遍历元素的时候,是生成iterator,然后使用iterator遍历。在生成iterator的时候,

会保存一个expectedModCount参数,这个是生成iterator的时候List中修改元素的次数。如果你在遍历过程中删除元素,List中modCount就会变化,如果这个modCount和exceptedModCount不一致,就会抛出异常,这个是为了安全的考虑。

看看list的remove源码:

 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
}

remove操作导致modCount++

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;
  public boolean hasNext() {
  return cursor != size;
  }
  public E next() {
checkForComodification();
try {
E next = get(cursor);
lastRet = cursor++;
return next;
} catch (IndexOutOfBoundsException e) {
checkForComodification();
throw new NoSuchElementException();
}
}   final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
} 

 总结:  1 foreach遍历,iterator遍历都不能在遍历的过程中使用list.remove或list.add操作,会报并发修改异常。

    2 iterator遍历过程中如果需要删除可以使用iterator提供的remove()方法。

    3 遍历根据元素索引删除是可行的。

以上属于个人心得,不对之处还望大佬指出,如果对你有帮助会更加激励我。  

 

集合并发修改异常-foreach的时候不可修改值的更多相关文章

  1. ConcurrentModificationException 集合并发修改异常 解决

    import java.util.ArrayList; import java.util.List; import java.util.ListIterator; /** * 问题? * 有一个集合, ...

  2. 集合框架之——迭代器并发修改异常ConcurrentModificationException

    问题: 我有一个集合,如下,请问,我想判断里面有没有"world"这个元素,如果有,我就添加一个"javaee"元素,请写代码实现. 使用普通迭代器出现的异常: ...

  3. java 15 - 8 集合框架(并发修改异常的产生原因以及解决方案)

    问题?   我有一个集合,如下,请问,我想判断里面有没有"world"这个元素,如果有,我就添加一个"javaee"元素,请写代码实现.  面试题: Concu ...

  4. Java基础知识强化之集合框架笔记19:List集合迭代器使用之 并发修改异常的产生原因 以及 解决方案

    1. 我有一个集合,如下,请问,我想判断里面有没有"world"这个元素,如果有,我就添加一个"javaee"元素,请写代码实现. ConcurrentModi ...

  5. ArrayList在foreach删除倒数第二个元素不抛并发修改异常的问题

    平时我们使用ArrayList比较多,但是我们是否知道ArrayList在进行foreach的时候不能直接通过list的add或者move方法进行删除呢, 原因就是在我们进行foreach遍历的时候, ...

  6. List集合遍历时修改元素出现并发修改异常总结

    什么是并发修改异常: 当我们在遍历实现了collection接口与iterator接口的集合时(List.Set.Map), 我们可以通过遍历索引也可以通过迭代器进行遍历.在我们使用迭代器进行遍历集合 ...

  7. 大杂烩 -- Iterator 并发修改异常ConcurrentModificationException

    基础大杂烩 -- 目录 大杂烩 -- Java中Iterator的fast-fail分析 大杂烩 -- Iterator 和 Iterable 区别和联系 问题: 在集合中,判断里面有没有" ...

  8. 日历类和日期类转换 并发修改异常 泛型的好处 *各种排序 成员和局部变量 接口和抽象类 多态 new对象内存中的变化

    day07 ==和equals的区别? ==用于比较两个数值 或者地址值是否相同.  equals 用于比较两个对象的内容是否相同   String,StringBuffer.StringBuilde ...

  9. ConcurrentModificationException(并发修改异常)的解决

    [异常解释] ConcurrentModificationException:当方法检测到对象的并发修改,但不允许这种修改时,抛出此异常.[产生的原因] 迭代器是依赖于集合而存在的,在判断成功后,集合 ...

随机推荐

  1. C#:CsvReader读取.CSV文件并转换成DataTable

    原文引用:https://www.codeproject.com/Articles/9258/A-Fast-CSV-Reader using LumenWorks.Framework.IO.Csv; ...

  2. AutoFac+ASP.NetMvc,AspNet.Core

    ASP.Net.Mvc 引用 install-package autofac install-package Mvc5 //创建一个用于注册的对象 ContainerBuilder builder = ...

  3. 《程序员面试金典(第5版)》【PDF】下载

    <程序员面试金典(第5版)>[PDF]下载链接: https://u253469.pipipan.com/fs/253469-230382252 内容简介 本书作者Gayle Laakma ...

  4. 推荐的五款市面上常用的免费CMS建站系统

    我做设计也有不少年头了,很多客户或者朋友找我做网站的时候,一般问我的是用什么软件系统给他们做.大部分客户希望用的软件是免费的.所以今天给大家介绍五款我自己用过还不错的,重点是还免费的建站系统. Met ...

  5. Hibernate框架HQL语句

    这篇随笔将会记录hql的常用的查询语句,为日后查看提供便利. 在这里通过定义了三个类,Special.Classroom.Student来做测试,Special与Classroom是一对多,Class ...

  6. 解决vue.js修改数据无法触发视图

    data:{checkValue:{}}that.checkValue[key] = [] 赋值无法实时改变变量:(数据其实最终被修改,但是并没有触发检测从而更新视图)原因:Vue 不能检测到对象属性 ...

  7. 排查程序死循环,死锁的方法 ——pstack

    pstack命令可显示每个进程的栈跟踪,pstack $pid即可,pstack命令须由$pid进程的属主或者root运行. 这次出现cpu占比100%的情况,但看memory占比,并无异常,怀疑是某 ...

  8. UVA 10891 Game of Sum

    题目大意就是有一个整数串,有两个人轮流取,每次可以取走一个前缀或后缀.两人都足够聪明,且都会使自己收益最大.求取完后先手比后手多多少. 每次我看见上面那句就会深感自己的愚笨无知. 所以来推推性质? 1 ...

  9. iis部署php项目

    今天跟着学习了如何在IIS下部署php项目,操作很简单,记录一下步骤! 1.启动iis服务器 最后点击确定就完成了 2.打开iis 点击进入即可 3.创建网站 进入添加网站. 添加注意事项如图所示! ...

  10. Fragment生命周期及实现点击导航图片切换fragment,Demo

    PS:Fragment简介 Fragment是Android3.0后引入的一个新的API,他出现的初衷是为了适应大屏幕的平板电脑, 当然现在他仍然是平板APP UI设计的宠儿,而且我们普通手机开发也会 ...