简介

或许有很多小伙伴都尝试过如下的代码:

ArrayList<Object> list = ...;
for (Object object : list) {
if (条件成立) {
list.remove(object);
}
}

然后会发现抛出java.util.ConcurrentModificationException异常,这是一个并发异常。那么这个到底是什么情况?首先需要介绍一下增强for循环

增强for循环

增强for循环是Java1.5后,Collection实现了Iterator接口后出现的。增强for循环的代码如下

for (Object object : list) {
// 操作
}

其实增强for循环就是使用Iterator迭代器进行迭代的,增强for循环就变成下面这样:

Iterator<Object> iterator = list.iterator();
while (iterator.hasNext()) {
iterator.next();
// 操作
}

那么为什么在增强for循环中调用list.remove(object)会出事呢?那么咱们看看ArrayList下的 Iterator的实现类: Itr类

Itr子类

Itr子类是Iterator的实现类,属于ArrayList私有的局部内部类。我截取了Itr类的部分代码,如下:

private class Itr implements Iterator<E> {
int cursor; // index of next element to return
int expectedModCount = modCount; Itr() {} public boolean hasNext() {
return cursor != size;
} @SuppressWarnings("unchecked")
public E next() {
checkForComodification();
...
} final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}

elementData 是ArrayList存放元素的数组,上面代码没有贴出来。

size 是elementData实际存放的容量大小

modCount 记录elementData容量的修改次数

expectedModCount 记录实例化迭代器Itr时,elementData容量的修改次数

注意!:在迭代器中,当执行next方法的时候,会去调用checkForComodification方法,判断elementData 的容量是否被修改过。



然后来看看ArrayList的remove(object)方法,截取部分代码如下:

public boolean remove(Object o) {
for (int index = 0; index < size; index++)
if (找到目标元素) {
fastRemove(index);
return true;
}
return false;
} private void fastRemove(int index) {
modCount++;
// 移除操作
}

可以发现,调用remove(object)方法时调用了fastRemove方法,在fastRemove方法中执行modCount++

现在把文章开头的代码拷下来,再来分析一次:

ArrayList<Object> list = ...;
for (Object object : list) {
if (条件成立) {
list.remove(object);
}
}

当执行了list.remove时,执行modCount++ 。此时迭代器再往下进行迭代,执行了next方法,发现 modCount != expectedModCount,那么则抛出java.util.ConcurrentModificationException异常。 之所以Iterator认为是一个并发异常。是因为你不在迭代器里操作,而是在迭代器外面进行remove操作呀!

难道没有其他解决方案吗?有滴。

解决方案

那么就是使用Itr的 remove方法。Itr子类重写了 remove 方法,这里部分代码:

public void remove() {
...
try {
ArrayList.this.remove(需要删除元素的索引);
...
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}

其实很简单,就是remove后,把 expectedModCount 同步一下 modCount 的值,这就解决了。完整代码如下:

ArrayList<Object> list = ...;
Iterator<Object> iterator = list.iterator();
while (iterator.hasNext()) {
iterator.next();
iterator.remove();
}

总结

本来我还不知道增强for循环是调用Iterator进行迭代的,要不是我debug了一波,我还不知道呐。还是小有收货。

个人博客网址: https://colablog.cn/

如果我的文章帮助到您,可以关注我的微信公众号,第一时间分享文章给您

执行ArrayList的remove(object)方法抛异常?的更多相关文章

  1. CloudStack的VO在调用setRemoved方法抛异常的原因

    今天在开发中发现一个问题,本来想对一个VO对象的removed值赋值,然后去update一下这条记录,一个最简单的set方法,但是在调用时直接抛异常了. 1: public void setRemov ...

  2. [Guava学习笔记]Basic Utilities: Null, 前置条件, Object方法, 排序, 异常

    我的技术博客经常被流氓网站恶意爬取转载.请移步原文:http://www.cnblogs.com/hamhog/p/3842433.html,享受整齐的排版.有效的链接.正确的代码缩进.更好的阅读体验 ...

  3. ABP在领域事件中异步调用方法抛异常

    在领域事件中调用UserRegistrationManager.RegisterAsync抛异常 Call UserRegistrationManager.RegisterAsync() throw ...

  4. ArrayList调用remove(int index)抛出UnsupportedOperationException问题分析以及解决记录

    使用Arrays转数组成为List后,不能调用add(...)和remove(...)方法,此时如果调用就会抛出UnsupportedOperationException异常 原因 其实Arrays. ...

  5. 抛异常 throw的注意事项

    子类覆盖父类只能抛出父类的异常或者子类或者子集注意:如果父类的方法没有抛异常,那么子类覆盖时绝对不能抛. 子类继承父类时,方法抛异常,要么抛父类,要么抛父类下的子类,不能抛父类平级或以上的异常 原因是 ...

  6. ArrayList调用remove方法需要注意的地方

    ArrayList中有remove 方法和 removeAll方法, ArrayList中不仅继承了接口Collection中的remove方法,而且还扩展了remove方法. Collection中 ...

  7. Java执行main方法,异常为:could not find the main class.program will exit

    未解决. Java执行方法,异常为:could not find the main class.program will exitmain 原文地址:http://rogerfederer.iteye ...

  8. 线程执行synchronized同步代码块时再次重入该锁过程中抛异常,是否会释放锁

    一个线程执行synchronized同步代码时,再次重入该锁过程中,如果抛出异常,会释放锁吗? 如果锁的计数器为1,抛出异常,会直接释放锁: 那如果锁的计数器为2,抛出异常,会直接释放锁吗? 来简单测 ...

  9. java ArrayList的remove()方法的参数为int和Integer的问题

    ArrayList的父类List中,有2个remove重载方法: remove(int index) remove(Object o) 假如参数输入为数字类型,到底是删除值等于该数字的对象还是删除索引 ...

随机推荐

  1. 河青的持久层框架hqbatis

    谈到对数据库的操作,powerbuilder 的嵌入式SQL还是最方便的,增.删.改.查都无比的方便,可惜它落败于BS架构的盛起.java 以mvc的框架,实现对数据库的操作,写起来是相当麻烦,jav ...

  2. 小程序-图片/文件本地缓存,减少CDN流量消耗

    写在前面 小程序网络图片读取: 在读取OSS图片CDN分发时流量大量消耗,导致资金费用增加. 网络图片比较大时,图片加载缓慢. 为了尽量减少上面两个问题,所以对已读的图片进行缓存处理,减少多次访问不必 ...

  3. C#数据结构与算法系列(二):稀疏数组(SparseArray)

    1.介绍 当一个数组中大部分元素为0,或者为同一个值的数组时,可以使用稀疏数组来保存该数组.    稀疏数组的处理方法是: 1.记录数组一共有几行几列,有多少个不同的值    2.把具有不同值的元素的 ...

  4. Stones【中石油个人赛第十七场I】

    Stones 题目链接(传送门) 来源:upc12899 题目描述 There are N stones arranged in a row. Every stone is painted white ...

  5. Stylus 之 网教通直播间整修

    暗色模式 效果 Mozilla 格式源代码 @-moz-document domain("fj.101.com") { * { transition: all .3s; } #wj ...

  6. Java++:安全|API接口安全性设计

    接口的安全性主要围绕 token.timestamp 和 sign 三个机制展开设计,保证接口的数据不会被篡改和重复调用,下面具体来看: Token授权机制: 用户使用用户名密码登录后服务器给客户端返 ...

  7. 这一次搞懂Spring的Bean实例化原理

    文章目录 前言 正文 环境准备 两个重要的Processor 注册BeanPostProcessor对象 Bean对象的创建 createBeanInstance addSingletonFactor ...

  8. nginx在windows系统中启动、重启、停止,常用命令

    cmd终端在进入到nginx的安装目录下使用对应命令 查看nginx的版本号:nginx -v 启动nginx:start nginx 快速停止或关闭nginx:nginx -s stop 正常停止或 ...

  9. 明文暴露___JS前台加密,java后台解密实现

    1.前台JS <script type="text/javascript"> $(function() { $("#btn").click(func ...

  10. spring boot actuator端点高级进阶metris指标详解、git配置详解、自定义扩展详解

    https://www.cnblogs.com/duanxz/p/3508267.html 前言 接着上一篇<Springboot Actuator之一:执行器Actuator入门介绍>a ...