文档章节

java修改集合抛出ConcurrentModificationException异常

Hyacinth_Yuan
 Hyacinth_Yuan
发布于 2015/08/19 16:45
字数 475
阅读 94
收藏 4

测试代码为:

public static void main(String[] args) {
		List<String> strList = new ArrayList<String>();
		strList.add("1");
		strList.add("2");
		strList.add("3");
		strList.add("4");
		for(String str:strList){
			if(str.equals("4")){
				strList.remove(str);
			}
		}
	}

运行后结果如下:

跟踪代码,抛出异常的地方具体在:

public E next() {
            checkForComodification();
	    try {
		E next = get(cursor);
		lastRet = cursor++;
		return next;
	    } catch (IndexOutOfBoundsException e) {
		checkForComodification();
		throw new NoSuchElementException();
	    }
	}

遍历时返回一个元素之前,会调用checkForComodification()函数进行检查,该函数实现为:

final void checkForComodification() {
	    if (modCount != expectedModCount)
		throw new ConcurrentModificationException();
	}

    modCount 为ArrayList修改的次数,expectedModCount 为期望修改的次数,在删除第四个元素之前,modCount 和expectedModCount 均为4(4次add),而调用ArrayList的remove()方法后,modCount 变为5,但该方法未修改expectedModCount ,其值仍为4,因此出现上述异常。

    解决方法为:单线程环境中,使用 Iterator的remove()方法取代ArrayList的remove()方法,可以避免ConcurrentModificationException异常。

    巧合的是,如果要删除的元素正好是集合中倒数第二个元素,则不会抛出此异常。Iterator遍历获取一个元素之前会先调用hasNext()方法来判断是否还有元素,该方法实现为:

public boolean hasNext() {
            return cursor != size();
	}

其中cursor为遍历的位置,从0开始,在上面的next()方法中被更新。

    删除倒数第二个元素"3"后,已经访问了3个元素,cursor指向下一个应该访问的元素位置,即3,而size()也返回3,hasNext()返回false,就不会调用next()方法,因此也不存在ConcurrentModificationException异常

    删除元素"4"后,cursor的值变为4,而size()返回3,hasNext()返回true,因此调用next()方法时就会抛出ConcurrentModificationException异常



附上一篇资料:

Java ConcurrentModificationException异常原因和解决方法:

http://www.cnblogs.com/dolphin0520/p/3933551.html








© 著作权归作者所有

Hyacinth_Yuan
粉丝 1
博文 14
码字总数 916
作品 0
闵行
程序员
私信 提问
为什么阿里禁止在 foreach 循环里进行元素的 remove/add 操作

在阿里巴巴Java开发手册中,有这样一条规定: 但是手册中并没有给出具体原因,本文就来深入分析一下该规定背后的思考。 1.foreach循环 foreach循环(Foreach loop)是计算机编程语言中的一种...

架构师springboot
05/10
66
2
HashMap和Hashtable的区别

HashMap和Hashtable的比较是Java面试中的常见问题,用来考验程序员是否能够正确使用集合类以及是否可以随机应变使用多种思路解决问题。HashMap的工作原理、ArrayList与Vector的比较以及这个问...

LCZ777
2014/03/29
92
0
CopyOnWriteArrayList简述

CopyOnWriteArrayList简单阅读源码 CopyOnWriteArrayList是Java并发包中提供的一个并发容器,它是个线程安全且读操作无锁的ArrayList,写操作则通过创建底层数组的新副本来实现,是一种读写分...

若杰
06/27
17
0
HashMap vs ConcurrentHashMap — 示例及Iterator探秘

如果你是一名Java开发人员,我能够确定你肯定知道ConcurrentModificationException,它是在使用迭代器遍历集合对象时修改集合对象造成的(并发修改)异常。实际上,Java的集合框架是迭代器设...

恶魔永生
2015/01/30
132
0
ArrayList源码解析之fail-fast机制深入理解

概要 前面,我们已经学习了ArrayList。接下来,我们以ArrayList为例,对Iterator的fail-fast机制进行了解。 1 fail-fast简介 fail-fast 机制是java集合(Collection)中的一种错误机制。 当多个...

sihailoveyan
2018/05/09
29
0

没有更多内容

加载失败,请刷新页面

加载更多

OSChina 周日乱弹 —— 我,小小编辑,食人族酋长

Osc乱弹歌单(2019)请戳(这里) 【今日歌曲】 @宇辰OSC :分享娃娃的单曲《飘洋过海来看你》: #今日歌曲推荐# 《飘洋过海来看你》- 娃娃 手机党少年们想听歌,请使劲儿戳(这里) @宇辰OSC...

小小编辑
今天
148
8
spring cloud

一、从面试题入手 1.1、什么事微服务 1.2、微服务之间如何独立通讯的 1.3、springCloud和Dubbo有哪些区别 1.通信机制:DUbbo基于RPC远程过程调用;微服务cloud基于http restFUL API 1.4、spr...

榴莲黑芝麻糊
今天
2
0
Executor线程池原理与源码解读

线程池为线程生命周期的开销和资源不足问题提供了解决方 案。通过对多个任务重用线程,线程创建的开销被分摊到了多个任务上。 线程实现方式 Thread、Runnable、Callable //实现Runnable接口的...

小强的进阶之路
昨天
6
0
maven 环境隔离

解决问题 即 在 resource 文件夹下面 ,新增对应的资源配置文件夹,对应 开发,测试,生产的不同的配置内容 <resources> <resource> <directory>src/main/resources.${deplo......

之渊
昨天
8
0
详解箭头函数和普通函数的区别以及箭头函数的注意事项、不适用场景

箭头函数是ES6的API,相信很多人都知道,因为其语法上相对于普通函数更简洁,深受大家的喜爱。就是这种我们日常开发中一直在使用的API,大部分同学却对它的了解程度还是不够深... 普通函数和...

OBKoro1
昨天
7
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部