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 遍历根据元素索引删除是可行的。

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

 

为什么iterator,foreach遍历时不能进行remove操作?除了一种情况可以这样(特殊情况)?的更多相关文章

  1. Iterator,foreach遍历小计

    此博客对同一操作对比两种遍历方式,以个人忘记时快速捡起为目的. 数据表: 三个List: List<Menu> menuList=menuService.getAllMenus(query ...

  2. 阿里规范学习总结-不要再foreach对元素进行add()/remove()操作,

    在foreach循环中,对元素进行 remove()/add() 操作需要使用Iterator ,如果运行在多线程环境下,需要对Iterator对象枷锁. public class ForeachTe ...

  3. 有关集合的foreach循环里的add/remove

    转自:Hollis(微信号:hollischuang) 在阿里巴巴Java开发手册中,有这样一条规定: 但是手册中并没有给出具体原因,本文就来深入分析一下该规定背后的思考. 1 .foreach循环 ...

  4. 关于for与forEach遍历集合中对集合进行操作的问题

    遍历List集合,在循环中再对List集合进行操作,有时候会遇到ConcurrentModificationException(并发修改异常);其实只有在forEach循环集合再对集合操作会发生异常: ...

  5. List remove操作注意问题

    public static void main(String[] args) { // TODO Auto-generated method stub List<String> list ...

  6. 用<forEach>遍历list集合时,提示我找不到对象的属性

    <c:forEach items="${list}" var="item"> <tr> <td>${item.UserId} ...

  7. java 集合list遍历时删除元素

    本文探讨集合在遍历时删除其中元素的一些注意事项,代码如下 import java.util.ArrayList; import java.util.Iterator; import java.util ...

  8. 【Java】List遍历时删除元素的正确方式

    当要删除ArrayList里面的某个元素,一不注意就容易出bug.今天就给大家说一下在ArrayList循环遍历并删除元素的问题.首先请看下面的例子: import java.util.ArrayLi ...

  9. 【原理探究】女朋友问我ArrayList遍历时删除元素的正确姿势是什么?

    简介 我们在项目开发过程中,经常会有需求需要删除ArrayList中的某个元素,而使用不正确的删除方式,就有可能抛出异常.或者在面试中,会遇到面试官询问遍历时如何正常删除元素.所以在本篇文章中,我们会 ...

随机推荐

  1. maven 基本框架搭建

    在平时的开发中还是在写blog时,在项目实例开始都会需要一遍一遍的介绍maven框架搭建,重复性的工作让我觉得烦恼,现在展现一下Java的核心思想“重复利用”,将这个重复性的描述提取出来一次性介绍,以 ...

  2. JDK的图文安装教程

    JDK的安装 什么是JDK? JDK就是Java开发工具包,即Java Development Kit.就是做Java开发所需要的最基本的工具.包括Java编译器(把人使用的Java语言变成JVM能运 ...

  3. 升级PHP版本导致zabbix无法访问解决办法

    故障现象:无法打开zabbix首页,提示缺少zabbix.conf配置文件 原因分析:升级yum安装php版本了,升级前卸载了原PHP5.4版本导致 解决办法: 重新安装zabbix yum inst ...

  4. SaltStack远程执行-模块

    上一篇:SaltStack数据系统-Pillar 执行模块 salt 'linux-node2.example.com' service.status sshd 其中service是模块名称statu ...

  5. C语言实现单链表(带头节点)

    C语言在实现单链表存储时需要注意的几点: 1.定义结构体,typedef:用于给结构体另命名 // 定义结构体类型 typedef struct Node{ int data; struct Node ...

  6. 使用RODBC连接MySQL数据库(R-3.4.3)

    1.安装RODBC包 install.packages("RODBC") 2.配置ODBC 首先查看是否有mysqlODBC5.1Driver  如果没有mysqlODBC5.1D ...

  7. DNS服务简介

    一.域名系统 1.域名系统概述 域名系统DNS(Domain Name System)是因特网使用的命名系统,用来把便于人们使用的机器名字转换成为IP地址.域名系统其实就是名字系统.为什么不叫“名字” ...

  8. HDU2842—Chinese Rings

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2842 题目意思:一把一个n连环的前n个拿下来,一个个n连环,要把第k个拿下来,需要把前n-2个拿下来, ...

  9. linux, sysrq,acpi,apci,uio,subsystem daemon

    linux, sysrq,acpi,apci Linux设备模型  一.sysfs文件系统: sysfs文件系统是Linux2.6内核引入的,它被看成是与proc.devfs和devpty等同类别的文 ...

  10. MapReduce分析流量汇总

    一.MapReduce编程规范 一.MapReduce编程规范 用户编写mr程序主要分为三个部分:Mapper,Reducer,Driver 1.Mapper阶段 (1)用户自定义Mapper类 要继 ...