Java中集合删除元素时候关于ConcurrentModificationException的迷惑点
下面的示例来至于阿里巴巴Java开发手册的集合处理部分的第7条:

运行如下代码,会发现正确运行。
public static void hasNotExcption() {
List<String> list1 = new ArrayList<String>();
list1.add("1");
list1.add("2");
for (String item : list1) {
System.out.println("item : "+item);
if ("1".equals(item)) {
list1.remove(item);
}
}
}
但是运行如下代码,则异常:java.util.ConcurrentModificationException(和1中的代码区别是上面移除判断条件是1,下面的判断条件是2)
public static void hasExcption() {
List<String> list1 = new ArrayList<String>();
list1.add("1");
list1.add("2");
for (String item : list1) {
System.out.println("item : "+item);
if ("2".equals(item)) {
list1.remove(item);
}
}
}
再看如下代码示例,运行结果见注释:
/**
*没有异常
*/
public static void hasNotExcption() {
List<String> list1 = new ArrayList<String>();
list1.add("1");
list1.add("2");
list1.add("3");
list1.add("4"); for (String item : list1) { System.out.println("item : " + item);
// 移除1和3时候会抛ConcurrentModificationException异常
// 但是移除2的时候不会抛出异常
if ("3".equals(item)) {
list1.remove(item);
}
} } /**
* 有异常
*/
public static void hasExcption1() {
List<String> list1 = new ArrayList<String>();
list1.add("1");
list1.add("2");
list1.add("3"); for (String item : list1) {// 增强的for循环底层实现使用的是迭代器
if ("1".equals(item)) {
// 移除并修改“modCount变量”,导致下次遍历时候异常
list1.remove(item);
}
} } /**
* 有异常
*/
public static void hasExcption2() {
List<String> list1 = new ArrayList<String>();
list1.add("1");
list1.add("2");
list1.add("3"); for (String item : list1) {
if ("3".equals(item)) {
list1.remove(item);
}
} }
通过上面三个例子是不是发现了结论?只要移除的是倒数第二个元素的话,就不会发生异常!的确是,移除倒数第二个元素的话就不会异常,那究竟是为什么呢?
知识点1:关于增强for循环的实现
集合的增强for循环实现内部使用的是迭代器,可以通过eclipse的F5调试跟踪。
List<String> list1 = new ArrayList<String>();
list1.add("1");
list1.add("2");
list1.add("3");
for (String item : list1) {
if ("3".equals(item)) {
list1.remove(item);
}
}
知识点2:集合如果判定的是并发修改错误?
细心的话可以通过异常发现异常的判断代码是:
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
其中的modCount是记录集合的修改次数,而expectedModCount是记录的预期的修改次数,当集合的修改次数和预期修改次数不一致的时候则发生异常。
那么什么时候会修改modCount呢?当我们对集合进行删除或者添加元素时候此时记录集合的修改次数就会发生增加,但是期望的修改次数不会变化,当检查判断异常条件时候就会根据条件处理。
到此回归到上面问题,对于hasExcption1和hasExcption2是如何发生异常的?本例对hasExcption1进行详细解释:
/**
* 有异常
*/
public static void hasExcption1() {
List<String> list1 = new ArrayList<String>();
list1.add("1");
list1.add("2");
list1.add("3");
// 增强的for循环底层实现使用的是迭代器。所以每一次都是调用一个hasNext,然后在调用next方法返回元素给item
for (String item : list1) { if ("1".equals(item)) {
// 移除并修改“modCount变量”,导致下次遍历时候异常
list1.remove(item);
}
} }
增强的for循环底层实现使用的是迭代器。所以每一次都是调用一个hasNext,然后在调用next方法返回元素给item。当我们删除元素之后,下一次循环时候会先调用hasNext是否还有元素,此时还有。在调用next方法返回该元素给item,但是调用next方法时候会检测
集合修改次数和预期修改次数是否相等,如果不等的话则抛出异常。(本例是不等的,因为移除1之后modCount=4,而expectModCount=3)。
那么为什么删除倒数第二个元素不会异常呢?示例代码:
/**
*没有异常
*/
public static void hasNotExcption() {
List<String> list1 = new ArrayList<String>();
list1.add("1");
list1.add("2");
list1.add("3");
list1.add("4"); for (String item : list1) {
if ("3".equals(item)) {
list1.remove(item);
}
} }
因为我们删除倒数第二个元素时候,此时size=size-1,当删除完元素进入下一次循环时候,此时hasNext方法判断是否还有元素的时候返回是false(hasNext使用的是游标和size进行比较)。所以就不会调用next方法了。上面例子我们知道异常就是在next方法中抛的,所以
删除倒数第二个元素就不会有异常。
注意:如果删除的是倒数第二个元素那么最后的结果是否正确呢?答案:显然是不正确的。
通过如下示例代码检验:
/**
*没有异常
*/
public static void hasNotExcption() {
List<String> list1 = new ArrayList<String>();
list1.add("1");
list1.add("2");
list1.add("3");
list1.add("3"); for (String item : list1) {
if ("3".equals(item)) {
list1.remove(item);
}
}
// 显然最后一个3没有删除,所以结果错误
System.out.println(list1); //[1, 2, 3] }
结果是错误的,原因:因为最后一个元素由于在移除倒数第二个元素时候将size-1了,所以遍历集合提前结束了。所以没有完全移除掉所有的“3”
Java中集合删除元素时候关于ConcurrentModificationException的迷惑点的更多相关文章
- java操作数组转list集合删除元素报错ConcurrentModificationException
public static void main(String[] args) { ArrayList<String> list = new ArrayList<String>( ...
- JAVA中循环删除list中元素的方法总结【转】
印象中循环删除list中的元素使用for循环的方式是有问题的,但是可以使用增强的for循环,然后今天在使用时发现报错了,然后去科普了一下,再然后发现这是一个误区.下面就来讲一讲..伸手党可直接跳至文末 ...
- java中循环删除list中元素的方法
重点哈 印象中循环删除list中的元素使用for循环的方式是有问题的,但是可以使用增强的for循环,然后今天在使用时发现报错了,然后去科普了一下,再然后发现这是一个误区.下面就来讲一讲..伸手党可直接 ...
- Java中集合的概述
一.集合和数组的区别 1.数组(可以存储基本数据类型)是用来存现对象的一种容器,但是数组的长度固定,不适合在对象数量未知的情况下使用. 2.集合(只能存储对象,对象类型可以不一样)的长度可变,可在多数 ...
- Java中集合List,Map和Set的区别
Java中集合List,Map和Set的区别 1.List和Set的父接口是Collection,而Map不是 2.List中的元素是有序的,可以重复的 3.Map是Key-Value映射关系,且Ke ...
- JAVA中集合转数组遍历
JAVA中集合的遍历的一种方法时集合转数组遍历,也是就调用Collection中的toArray(). 代码: public static void main(String[] args) { ...
- Java中集合List,Map和Set的差别
Java中集合List,Map和Set的差别 1.List和Set的父接口是Collection.而Map不是 2.List中的元素是有序的,能够反复的 3.Map是Key-Value映射关系,且Ke ...
- 遍历List集合删除元素的出现报错
遍历List集合删除元素的出现报错 遍历List集合删除元素的时候会发生索引越界异常或内容遍历不全等问题. 例子: List<String> al = new ArrayList< ...
- js 遍历集合删除元素
js 遍历集合删除元素 /** * 有效的方式 - 改变下标,控制遍历 */ for (var i = 0; i < arr.length; i++) { if (...) { arr.spli ...
随机推荐
- 自动化运维(2)之一键式单实例安装MySQL
ZMySQLAutoTools文档 目标:自动化构建部署MySQL数据库,一键式单实例mysql安装,备份,监控,主从集群部署等.以及jdk,tomcat,nginx等基础中间件的自动化部署安装及运维 ...
- SPI OLED 驱动
根据之前说过的 SPI 驱动的框架,在我们添加 SPI 设备驱动的时候需要与 SPI Master 完成匹配,通过 spi_register_board_info 进行注册. 构造设备 static ...
- java_二进制的前导的零
题目内容: 计算机内部用二进制来表达所有的值.一个十进制的数字,比如18,在一个32位的计算机内部被表达为00000000000000000000000000011000.可以看到,从左边数过来,在第 ...
- Python编程-从入门到实践 Eric Matthes 著 袁国忠 译 - - 第二章 动手试一试
因为第一章的动手试一试基本都是探索性的,所以直接进入第二章. # 2.2 动手试一试 # 2_1 简单消息: 将一条消息存储到变量中,再将其打印出来. message = 'python 编程从入门到 ...
- linux学习笔记-文件相关知识
我的邮箱地址:zytrenren@163.com欢迎大家交流学习纠错! 一.文件属性 在当前用户家目录下以ls -al命令输出为例: -rw-r--r-- 1 renren ...
- 活字格Web应用平台学习笔记 8 - 查询记录
基础教程只剩一点点了,学完算了. 这一课的目标是:查询记录 这个操作的原理很简单,增加一个表格,绑定“部门”那个数据,然后增加一个命令,点击查询. 绑定命令: 完成后的实际界面,查询前面是个下拉框: ...
- RxJava2.0的使用详解
RxJava2.0的使用详解 1,初识RxJava RxJava就是一种用Java语言实现的响应式编程,来创建基于事件的异步程序 RxJava是一个基于事件订阅的异步执行的一个类库,目前比较火的一些技 ...
- 浅谈Arrays.asList()方法的使用
首先,该方法是将数组转化为list.有以下几点需要注意: (1)该方法不适用于基本数据类型(byte,short,int,long,float,double,boolean) (2)该方法将数组与列表 ...
- 阿里SopHix热修复框架
2015年以来,Android开发领域里对热修复技术的讨论和分享越来越多,同时也出现了一些不同的解决方案,如QQ空间补丁方案.阿里AndFix以及微信Tinker(Bugly sdk也集成Tikner ...
- wx python 基本控件
一.静态文本控件 wx.StaticText(parent, id, label, pos=wx.DefaultPosition, size=wx.DefaultSize, style=0, n ...