List remove ConcurrentModificationException源码分析
Exception in thread "main" java.util.ConcurrentModificationException
at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:909)
at java.util.ArrayList$Itr.next(ArrayList.java:859)
at com.mashibing.tank.Test2.main(Test2.java:14)
ArrayList在迭代删除元素时,如果使用不当会发生concurrentModification 异常,常见的错误使用场景
Iterator<Integer> iterator = list.iterator();
while(iterator.hasNext()){
Integer next = iterator.next();
list.remove(next);
}
for (Integer next:list
) {
list.remove(next);
}
发生异常原因:
首先明确以上两种方式是等效的,原因:增强for在反编译后语句
Iterator var4 = var1.iterator();
while(var4.hasNext()) {
Integer var3 = (Integer)var4.next();
var1.remove(var3);
}
然后我们去分析发生concurrentModification原因:
由于异常发生在 :java.util.ArrayList$Itr.checkForComodification(ArrayList.java:909),我们先去看下此处代码
发现remove(intxOrObject) 方法为 ArrayList 内部类Itr的一个方法 ,当modCount != expectedModCount 时会抛出异常ConcurrentModificationException。
先来解释下概念:
modCount:为 成员变量 list 被修改的次数 ,每次add ,remove都会+1 (The number of times this list has been )
public abstract class AbstractList<E> extends AbstractCollection<E> implements List<E>{
//The number of times this list has been
protected transient int modCount = 0;
}
expectedModCount:为listIterator 方法 局部变量,表示对ArrayList修改次数的期望值,它的初始值为modCount
public Iterator<E> iterator() {
return listIterator();
}
public ListIterator<E> listIterator(final int index) {
checkForComodification();
rangeCheckForAdd(index);
final int offset = this.offset;
return new ListIterator<E>() {
int cursor = index;
int lastRet = -1;
//注意这里
int expectedModCount = ArrayList.this.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;
Itr() {}
public boolean hasNext() {
return cursor != size;
}
@SuppressWarnings("unchecked")
public E next() {
checkForComodification();
int i = cursor;
if (i >= size)
throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length)
throw new ConcurrentModificationException();
cursor = i + 1;
return (E) elementData[lastRet = i];
}
}
这里假设原始list 长度为10(只做过新增,没有做过删除操作) 。
这时候最初 AbstractList.modCount =10 ;
Iterator.expectedModCount = modCount =10 ;
1.当第一次循环调用next时,无变动list ,所以expectedModCount = modCount =10 ,
2.当 arrayList.remove(indexOrObject)时,modCount++ 变为11 ,expectedModCount 依然为10 。
3.第二次调用next 时,Itr 会执行checkForComodification() ,也就是判断expectedModCount、 modCount 是否一致,如果不一致则抛出ConcurrentModificationException 。
正确姿势:
关注点:
1.增强for循环不要删除元素
2.使用Iterator<Integer> iterator = list.iterator(); 方式时,先获取元素,然后使用iterator.remove() 进行删除
Iterator<Integer> iterator = list.iterator();
while (iterator.hasNext() ) {
System.out.println(iterator.next());
iterator.remove();
}
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
list.remove(i);
}
为什么Iterator<Integer> iterator = list.iterator(); 方式时 用iterator remove 不会出现问题?看源码:
private class Itr implements Iterator<E> {
public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
ArrayList.this.remove(lastRet);
cursor = lastRet;
lastRet = -1;
//关注这里, Itr里将modCount 赋值给expectedModCount
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
}
为什么普通循环不会有ConcurrentModificationException ,看源码:
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
public boolean remove(Object o) {
if (o == null) {
for (int index = 0; index < size; index++)
if (elementData[index] == null) {
fastRemove(index);
return true;
}
} else {
for (int index = 0; index < size; index++)
if (o.equals(elementData[index])) {
fastRemove(index);
return true;
}
}
return false;
}
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
}
}
List remove ConcurrentModificationException源码分析的更多相关文章
- 集合操作出现的ConcurrentModificationException(源码分析)
摘要: 为了保证线程安全,在迭代器迭代的过程中,线程是不能对集合本身进行操作(修改,删除,增加)的,否则会抛出ConcurrentModificationException的异常. 示例: publi ...
- Java并发-ConcurrentModificationException原因源码分析与解决办法
一.异常原因与异常源码分析 对集合(List.Set.Map)迭代时对其进行修改就会出现java.util.ConcurrentModificationException异常.这里以ArrayList ...
- HashMap与TreeMap源码分析
1. 引言 在红黑树--算法导论(15)中学习了红黑树的原理.本来打算自己来试着实现一下,然而在看了JDK(1.8.0)TreeMap的源码后恍然发现原来它就是利用红黑树实现的(很惭愧学了Ja ...
- 【集合框架】JDK1.8源码分析之HashMap & LinkedHashMap迭代器(三)
一.前言 在遍历HashMap与LinkedHashMap时,我们通常都会使用到迭代器,而HashMap的迭代器与LinkedHashMap迭代器是如何工作的呢?下面我们来一起分析分析. 二.迭代器继 ...
- 【JUC】JDK1.8源码分析之CopyOnWriteArrayList(六)
一.前言 由于Deque与Queue有很大的相似性,Deque为双端队列,队列头部和尾部都可以进行入队列和出队列的操作,所以不再介绍Deque,感兴趣的读者可以自行阅读源码,相信偶了Queue源码的分 ...
- 【JUC】JDK1.8源码分析之ConcurrentSkipListSet(八)
一.前言 分析完了CopyOnWriteArraySet后,继续分析Set集合在JUC框架下的另一个集合,ConcurrentSkipListSet,ConcurrentSkipListSet一个基于 ...
- HashMap实现原理及源码分析
哈希表(hash table)也叫散列表,是一种非常重要的数据结构,应用场景及其丰富,许多缓存技术(比如memcached)的核心其实就是在内存中维护一张大的哈希表,而HashMap的实现原理也常常出 ...
- JDK Collection 源码分析(2)—— List
JDK List源码分析 List接口定义了有序集合(序列).在Collection的基础上,增加了可以通过下标索引访问,以及线性查找等功能. 整体类结构 1.AbstractList 该类作为L ...
- [源码解析]HashMap和HashTable的区别(源码分析解读)
前言: 又是一个大好的周末, 可惜今天起来有点晚, 扒开HashMap和HashTable, 看看他们到底有什么区别吧. 先来一段比较拗口的定义: Hashtable 的实例有两个参数影响其性能:初始 ...
随机推荐
- 视觉SLAM算法框架解析(3) SVO
版权声明:本文为博主原创文章,未经博主允许不得转载. SVO(Semi-direct Visual Odometry)[1]顾名思义是一套视觉里程计(VO)算法.相比于ORB-SLAM,它省去了回环检 ...
- lua https request 调用
网上资料 引用ssl.https 包 local https = require("ssl.https") 之后按同http一样调用. 但是,这种只最基本的实现了访问https服务 ...
- ionic2踩坑之文本域自适应高度(自定义指令,适用angular2)
话不多说,看代码: import { Directive, ElementRef, HostListener,Input, Renderer, Component } from '@angular/c ...
- <JZOJ5906>传送门
emmm dpdpdp然鹅我考场上并想不到 还是凉凉 #include<cstdio> #include<cmath> #include<iostream> #in ...
- 在腾讯云服务器上安装JDK+Tomcat并启动tomcat
由于Java web项目需要使用到tomcat所以决定在腾讯云服务器(centos7.X)上安装JDK和tomcat,来部署我们的项目. 准备工具: 云服务器:centos7.x+ 本地连接服务器:X ...
- Luogu_2876_[USACO07JAN]解决问题Problem Solving
题目描述 过去的日子里,农夫John的牛没有任何题目. 可是现在他们有题目,有很多的题目. 精确地说,他们有\(P(1 \leq P \leq 300)\)道题目要做. 他们还离开了农场并且象普通人一 ...
- MyBatis之pageHelper分页插件
1.先导入Maven,jar包依赖 <dependency> <groupId>com.github.pagehelper</groupId> <artifa ...
- ant:如何用ant将web project打包成war包
说明:src中的文件将不会呈现出来,诸位可以自己写一个简单的web项目,然后依照我所提供的ant脚本配置来设置. 文件结构如图所示: 配置为下: build.xml < ...
- 机器学习迁移模型到IOS
https://paulswith.github.io/2018/02/24/%E8%BF%81%E7%A7%BB%E5%AD%A6%E4%B9%A0InceptionV3/ 上文记录了如何从一个别人 ...
- zabbix监控mysql数据库信息脚本
---恢复内容开始--- 在/usr/local/zabbix/etc/zabbix_agentd.conf增加 # 获取mysql性能指标 UserParameter=mysql.status[*] ...