一、背景

在以前的随笔中说道过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内部类ItrhasNext(),该方法会对当前循环指针和长度做判断。只有当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正常迭代删除不报错的原因相关推荐

  1. HashMap遍历中删除元素报错

    文章目录 一.问题描述 二.问题分析 三.解决问题 四.相关问题 一.问题描述 HashMap在遍历的时候进行删除元素报错java.util.ConcurrentModificationExcepti ...

  2. php zpo框架,Yii使用DeleteAll连表删除出现报错问题的解决方法

    本文实例讲述了Yii使用DeleteAll连表删除出现报错问题的解决方法.分享给大家供大家参考,具体如下: 删除数据的时候,经常会遇到连联判断删除数据的条件,今天用Yii 的CDbCriteria生成 ...

  3. Ambari删除服务报错之CSRF protection is turned on

    Ambari安装组件失败后执行 curl 删除服务报错 CSRF protection is turned on X-Requested_By HTTP Header is required 解决方案 ...

  4. Docker下删除镜像报错 (cannot be forced) - image has dependent child images

    记录一次docker删除镜像报错: docker rmi 镜像Id 报错: Error response from daemon: conflict: unable to delete ae9561a ...

  5. 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 ...

  6. 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 ...

  7. linux svn e170001 认证失败,jenkins - svn: E170001报错的原因以及解决方案

    1. 什么问题What? 使用Jenkins配置的svn拉取项目,Jenkins报错:svn: E170001; Your credentials to connect to the reposito ...

  8. linux svn e170001,jenkins - svn: E170001报错的原因以及解决方案

    1. 什么问题What? 使用Jenkins配置的svn拉取项目,Jenkins报错:svn: E170001; Your credentials to connect to the reposito ...

  9. 安装Saas芸众商城系统后提示:{“result“:0,“msg“:““,“data“:{“status“:-4}}报错的原因

    安装Saas芸众商城系统至尊版所遇到的坑我想你也踩过,总结出来! 安装宝塔,本文是用的是lamp,可以酌情考虑. 放入代码和数据库. php使用7.4版本,安装 ionCube, fileinfo, ...

最新文章

  1. linux 下用户管理
  2. Python 比特币 教程 之一:创建机器人
  3. Android初学者教程
  4. Android之 AndroidManifest.xml 文件解析
  5. android开发toast通知,Toast Notifications
  6. Python入门100题 | 第052题
  7. C#开发微信门户及应用(44)--微信H5页面开发的经验总结
  8. 全球IPv4地址正式耗尽,你知道吗?
  9. 量子计算入门-第一部分
  10. mysql 密码1045_mysql登录1045错误时 修改登录密码
  11. 手机正在录音怎么隐藏
  12. ajax成功后没有执行函数,ajax不执行回调函数
  13. TensorFlow精进之路(一):Softmax回归模型训练MNIST
  14. 欧拉项目(python练习)problem 45
  15. python图形显示不出来_Matplotlib无法显示图像的问题
  16. 如何破解Word锁定文件的方法
  17. vue3项目将图片正时针、逆时针旋转
  18. Mac下嵌入式开发初步(二)
  19. H3CTE讲师分享H3C实验7 PPP
  20. 将文件复制到FTP服务器时发生错误。请检查是否有权限访问该文件夹 问题解决

热门文章

  1. matlab 整数规划工具箱,Matlab中的YALMIP工具箱 混合整数规划
  2. java集合迭代器_java集合迭代器
  3. python编码规范简单总结
  4. 计算机组成原理测试题
  5. 接口不能被实例化的吗?接口引用是什么?
  6. NYOJ-邮票分你一半(dp)
  7. 图像算法九:【图像特征提取】特征降维、PCA人脸特征抽取、局部二进制
  8. Could not load dynamic library ‘libcudart.so.10.0‘; dlerror: libcudart.so.10.0: cannot open shared o
  9. 用Docker容器自带的tensorflow serving部署模型对外服务(成功率100%)
  10. 【转载】QT 的信号与槽机制介绍