ArrayList在foreach正常迭代删除不报错的原因
一、背景
在以前的随笔中说道过ArrayList的foreach迭代删除的问题:ArrayList迭代过程删除问题
按照以前的说法,在ArrayList中通过foreach迭代删除会抛异常:java.util.ConcurrentModificationException
但是下面这段代码实际情况却没报异常,是什么情况?
1 List<String> list = new ArrayList<String>(); 2 list.add("1"); 3 list.add("2"); 4 for (String item : list) { 5 System.out.println("item:" + item); 6 if ("1".equals(item)) { 7 list.remove(item); 8 } 9 }
二、分析
我们知道ArrayList的foreach迭代调用的是ArrayList内部类Itr,Itr源码如下:
1 private class Itr implements Iterator<E> { 2 int cursor; // index of next element to return 3 int lastRet = -1; // index of last element returned; -1 if no such 4 int expectedModCount = modCount; 5 6 public boolean hasNext() { 7 return cursor != size; 8 } 9 10 @SuppressWarnings("unchecked") 11 public E next() { 12 checkForComodification(); 13 int i = cursor; 14 if (i >= size) 15 throw new NoSuchElementException(); 16 Object[] elementData = ArrayList.this.elementData; 17 if (i >= elementData.length) 18 throw new ConcurrentModificationException(); 19 cursor = i + 1; 20 return (E) elementData[lastRet = i]; 21 } 22 23 public void remove() { 24 if (lastRet < 0) 25 throw new IllegalStateException(); 26 checkForComodification(); 27 28 try { 29 ArrayList.this.remove(lastRet); 30 cursor = lastRet; 31 lastRet = -1; 32 expectedModCount = modCount; 33 } catch (IndexOutOfBoundsException ex) { 34 throw new ConcurrentModificationException(); 35 } 36 } 37 38 final void checkForComodification() { 39 if (modCount != expectedModCount) 40 throw new ConcurrentModificationException(); 41 } 42 }
按照调用顺序查看源码
1.hasNext
ArrayList的foreach迭代首先调用的是ArrayList内部类Itr的hasNext(),该方法会对当前循环指针和长度做判断。只有当hasNext()返回true才会执行foreach里面的代码,cursor = size时候就退出循环(因为这两者相等意味着都遍历完了,假如ArrayList的size=2,那么hasNext会被调用3次,但是第3次调用不会执行foreach里面代码)。
2.如果上一步返回true的话会执行Itr的next(),如果数据无异常的话 cursor = i + 1;所以没执行一次next()时cursor都会+1
3.接着会执行到 list.remove(item),此处调用的是ArrayList的remove(Object o)而不是Itr的,看下ArrayList的remove()的源码:
1 public boolean remove(Object o) { 2 if (o == null) { 3 for (int index = 0; index < size; index++) 4 if (elementData[index] == null) { 5 fastRemove(index); 6 return true; 7 } 8 } else { 9 for (int index = 0; index < size; index++) 10 if (o.equals(elementData[index])) { 11 fastRemove(index); 12 return true; 13 } 14 } 15 return false; 16 }
o != null,会进入else的fastRemove(index);可以看到ArrayList根据传入的值删除会进行遍历equals判断,找到索引再通过fastRemove(index)删除,因此List频繁做删除修改效率比较低。
4.再看下fastRemove()源码:
1 */ 2 private void fastRemove(int index) { 3 modCount++; 4 int numMoved = size - index - 1; 5 if (numMoved > 0) 6 System.arraycopy(elementData, index+1, elementData, index, 7 numMoved); 8 elementData[--size] = null; // clear to let GC do its work 9 }
第8行会把该索引对应的数组的值置为null,并且size-1。但是却没有进行 cursor - 1操作
至此明白了。此处的ArrayList通过foreach迭代删除为什么不会报错:
刚开始ArrayList的size=2时,cursor =0
①第一次hasNext()进来,cursor != size进入next()后cursor=1,接着因为满足条件删除的时候size-1=1;
②第二次hasNext()进来,cursor = size = 1,所以不会执行foreach的代码,也不会出现后面检测modCount值抛ConcurrentModificationException
上述未抛异常的情况主要是hasNext()中判断遍历完成的条件与ArrayList删除后的数据刚好吻合而已。
所以只要满足条件:删除的元素在循环时的指针cursor+1=size就会出现这种情况!删除ArrayList倒数第二个(即第 size - 1个元素)就会出现不抛异常的假象。
(例如size=3,删除第2个元素;size=4,删除第3个元素)
因为删除后size-1=cursor
public boolean hasNext() {
return cursor != size;
}
hasNext()会返回false,不会进入ArrayList的迭代器,就不会进入 next() 执行checkForComodification()
这是一种条件判断下的特殊情况,其他情况都会抛出异常,所以不要在foreach进行删除数据。请在迭代器中进行删除。
转载于:https://www.cnblogs.com/hupu-jr/p/8082187.html
ArrayList在foreach正常迭代删除不报错的原因相关推荐
- HashMap遍历中删除元素报错
文章目录 一.问题描述 二.问题分析 三.解决问题 四.相关问题 一.问题描述 HashMap在遍历的时候进行删除元素报错java.util.ConcurrentModificationExcepti ...
- php zpo框架,Yii使用DeleteAll连表删除出现报错问题的解决方法
本文实例讲述了Yii使用DeleteAll连表删除出现报错问题的解决方法.分享给大家供大家参考,具体如下: 删除数据的时候,经常会遇到连联判断删除数据的条件,今天用Yii 的CDbCriteria生成 ...
- Ambari删除服务报错之CSRF protection is turned on
Ambari安装组件失败后执行 curl 删除服务报错 CSRF protection is turned on X-Requested_By HTTP Header is required 解决方案 ...
- Docker下删除镜像报错 (cannot be forced) - image has dependent child images
记录一次docker删除镜像报错: docker rmi 镜像Id 报错: Error response from daemon: conflict: unable to delete ae9561a ...
- SQL Server 删除数据库报错 Cannot drop database dbname because it is currently in use
SQL Server 删除数据库报错 Cannot drop database "dbname" because it is currently in use SQL Server ...
- MySQL删除用户报错:ERROR 1396 (HY000): Operation DROP USER failed for ‘tsjt‘@‘127.0.0.1‘
背景: 本地安装的MySQL的MariaDB,期间创建了两个名为tsjt的不同登录ip的用户,一个为127.0.0.1,另一个为localhost.测试完毕后准备删除. 此时却发现创建的tsjt@12 ...
- linux svn e170001 认证失败,jenkins - svn: E170001报错的原因以及解决方案
1. 什么问题What? 使用Jenkins配置的svn拉取项目,Jenkins报错:svn: E170001; Your credentials to connect to the reposito ...
- linux svn e170001,jenkins - svn: E170001报错的原因以及解决方案
1. 什么问题What? 使用Jenkins配置的svn拉取项目,Jenkins报错:svn: E170001; Your credentials to connect to the reposito ...
- 安装Saas芸众商城系统后提示:{“result“:0,“msg“:““,“data“:{“status“:-4}}报错的原因
安装Saas芸众商城系统至尊版所遇到的坑我想你也踩过,总结出来! 安装宝塔,本文是用的是lamp,可以酌情考虑. 放入代码和数据库. php使用7.4版本,安装 ionCube, fileinfo, ...
最新文章
- linux 下用户管理
- Python 比特币 教程 之一:创建机器人
- Android初学者教程
- Android之 AndroidManifest.xml 文件解析
- android开发toast通知,Toast Notifications
- Python入门100题 | 第052题
- C#开发微信门户及应用(44)--微信H5页面开发的经验总结
- 全球IPv4地址正式耗尽,你知道吗?
- 量子计算入门-第一部分
- mysql 密码1045_mysql登录1045错误时 修改登录密码
- 手机正在录音怎么隐藏
- ajax成功后没有执行函数,ajax不执行回调函数
- TensorFlow精进之路(一):Softmax回归模型训练MNIST
- 欧拉项目(python练习)problem 45
- python图形显示不出来_Matplotlib无法显示图像的问题
- 如何破解Word锁定文件的方法
- vue3项目将图片正时针、逆时针旋转
- Mac下嵌入式开发初步(二)
- H3CTE讲师分享H3C实验7 PPP
- 将文件复制到FTP服务器时发生错误。请检查是否有权限访问该文件夹 问题解决
热门文章
- matlab 整数规划工具箱,Matlab中的YALMIP工具箱 混合整数规划
- java集合迭代器_java集合迭代器
- python编码规范简单总结
- 计算机组成原理测试题
- 接口不能被实例化的吗?接口引用是什么?
- NYOJ-邮票分你一半(dp)
- 图像算法九:【图像特征提取】特征降维、PCA人脸特征抽取、局部二进制
- Could not load dynamic library ‘libcudart.so.10.0‘; dlerror: libcudart.so.10.0: cannot open shared o
- 用Docker容器自带的tensorflow serving部署模型对外服务(成功率100%)
- 【转载】QT 的信号与槽机制介绍