foreach循环

​    foreach循环(Foreach loop)是计算机编程语言中的一种控制流程语句,通常用来循环遍历数组或集合中的元素。Java语言从JDK 1.5.0开始引入foreach循环。在遍历数组、集合方面,foreach为开发人员提供了极大的方便。通常也被称之为增强for循环。

​    在日常开发中,foreach循环用的非常多,但是有一点要非常小心,就是不能在这个循环里对数组或者集合里的元素进行remove或者add操作,否则会抛出java.util.ConcurrentModificationException

## 开发规范

​    在阿里巴巴java开发规范手册中有这样一种规定:

## 代码验证

package com.kobe.demo.collection;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List; public class TestForList { public static void main(String[] args) { List<String> list = new ArrayList<>(); list.add("111");
list.add("222"); for (String item : list) {
if ("222".equals(item)) {
list.remove(item);
}
} } }
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.kobe.demo.collection.TestForList.main(TestForList.java:15)

## 分析

​    因为foreach循环是Java提供的一种语法糖,所以我们用反编译工具将以上代码编译后看看:

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
// package com.kobe.demo.collection; import java.util.ArrayList;
import java.util.Iterator;
import java.util.List; public class TestForList {
public TestForList() {
} public static void main(String[] args) {
List<String> list = new ArrayList();
list.add("111");
list.add("222");
Iterator var2 = list.iterator(); while(var2.hasNext()) {
String item = (String)var2.next();
if ("222".equals(item)) {
list.remove(item);
}
} }
}

​    显然,foreach循环实际上还是用Iterator迭代器while循环。根据堆栈信息,查看源码,可以看到是当调用ArrayList里的内部类Itr的checkForComodification()方法报错:

​    那我们看看modCount和expectModCount是什么?

  - modCount是ArrayList中的一个成员变量。它表示该集合实际被修改的次数

  - expectedModCount 是 ArrayList中的一个内部类Itr中的成员变量。expectedModCount表示这个迭代器期望该集合被修改的次数。其值是在ArrayList.iterator方法被调用的时候初始化的。只有通过迭代器对集合进行操作,该值才会改变。

  - Itr是一个Iterator的实现,使用ArrayList.iterator方法可以获取到的迭代器就是Itr类的实例。

​    再看到remove方法的核心操作:

​    可以看到,它只修改了modCount,并没有对expectedModCount做任何操作。

## 总结
   foreach循环里,遍历集合实际上是通过**迭代器Iterator**进行的,但是元素的remove/add方法却是使用的是**集合自己**的方法,导致在遍历的时候,会发现某个元素自己神不知鬼不觉地被删除/增加了,这时候,就会抛出一个异常,告诉用户可能会用多个线程对同一个集合发生了并发修改。

foreach循环里不能remove/add元素的原理的更多相关文章

  1. 为什么阿里巴巴Java开发手册中强制要求不要在foreach循环里进行元素的remove和add操作?

    在阅读<阿里巴巴Java开发手册>时,发现有一条关于在 foreach 循环里进行元素的 remove/add 操作的规约,具体内容如下: 错误演示 我们首先在 IDEA 中编写一个在 f ...

  2. 不要在 foreach 循环里进行元素的 remove/add 操作。remove 元素请使用 Iterator 方式,如果并发操作,需要对 Iterator 对象加锁

    不要在 foreach 循环里进行元素的 remove/add 操作.remove 元素请使用 Iterator 方式,如果并发操作,需要对 Iterator 对象加锁. 正例: Iterator&l ...

  3. 为什么阿里巴巴禁止在 foreach 循环里进行元素的 remove/add 操作--java.util.ConcurrentModificationException

    摘要 foreach循环(Foreach loop)是计算机编程语言中的一种控制流程语句,通常用来循环遍历数组或集合中的元素. 在阿里巴巴Java开发手册中,有这样一条规定: 但是手册中并没有给出具体 ...

  4. 为什么禁止在 foreach 循环里进行元素的 remove/add 操作

    首先看下边一个例子,展示了正确的做法和错误的错发: 这是为什么呢,具体原因下面进行详细说明: 1.foreach循环(Foreach loop)是计算机编程语言中的一种控制流程语句,通常用来循环遍历数 ...

  5. 有关集合的foreach循环里的add/remove

    转自:Hollis(微信号:hollischuang) 在阿里巴巴Java开发手册中,有这样一条规定: 但是手册中并没有给出具体原因,本文就来深入分析一下该规定背后的思考. 1 .foreach循环 ...

  6. ArrayList之foreach循环删除倒数第二个元素,不触发fail-fast机制

    今天一朋友问了个问题,对于如下一段代码,运行后会有怎样的结果? public class ArrayListTest { public static void main(String[] args) ...

  7. JSTL中c:forEach循环里的值的substr操作及对String操作的常用API

    <c:forEach items="${dataList}" var="item" varStatus="itemStatus"> ...

  8. vector 循环里删除多个元素

    ; i < (int)vecLines.size(); i++) { AcDbLine * l1 = vecLines[i]; if (l1 == NULL) { continue; } //记 ...

  9. 腾讯一面!说说ArrayList的遍历foreach与iterator时remove的区别,我一脸懵逼

    本文基于JDK-8u261源码分析 1 简介 ​ ArrayList作为最基础的集合类,其底层是使用一个动态数组来实现的,这里"动态"的意思是可以动态扩容(虽然ArrayList可 ...

随机推荐

  1. 极客时间 深入拆解java虚拟机 一至三讲学习总结

    为什么要学习java虚拟机 1.学习java虚拟机的本质,是了解java程序是如何被执行且优化的.这样一来,才可以从内部入手,达到高效编程的目的.与此同时,你也可以为学习更深层级.更为核心的java技 ...

  2. python数组相关知识

    1.np中的reshape函数,可以把矩阵重新划分成m行n列. arange(n)可以把 [0,n-1]装入数组中,一定要注意的是img.reshape()并不会改变原来的数组,所以需要另外新建一个数 ...

  3. Cocos Creator 按钮音效封装重写

    cc.Button.prototype._onTouchEnded = function (t) { cc.hb.audioMgr.playMusic("click", false ...

  4. Cocos Creator 获取当前URL取参数

    利用Javascript获取当前页的URL,这个问题起来好像很复杂,如果第一次去想这个问题,很多人估计又在琢磨到底又是哪个神一般的Javascript函数. 其实不是,Javascript获取当前页的 ...

  5. java快排思想

    1分治思想 1.1比大小在分区 1.2从数组中取出一个数做基准数 1.3将比他小的数全放在他的左边,比他大的数全放在他的右边 1.4然后递归 左边 和右边 }

  6. python实现使用词云展示图片

    记录瞬间 首先,要安装一些第三方包 pip install scipyCollecting scipy Downloading https://files.pythonhosted.org/packa ...

  7. Jenkins - ERROR: Exception when publishing, exception message [Failure] Build step 'Send build artifacts over SSH' changed build result to UNSTABLE

    今天在处理Jenkins的时候出现了一些异常,看着控制台,编译都是通过的,只是没有部署上来,查看了控制台日志,如下: 刚开始还以为是权限通道什么的,后来才发现是执行脚本根本不让执行,以前也遇到过,都是 ...

  8. synchronized锁级别的一个坑

    在实现一次对限流接口访问时,我错误的使用了单例+synchronized修饰方法的形式实现,这样在限流方规则为不同接口不同限制,单独限制时,同一个实例中的所有被synchronized修饰的方法竞争同 ...

  9. VS2015右键集成TortoiseGit

    先上效果 再说步骤 1.安装VS TortoiseGit等~~ 2.以外部工具方式调用TortoiseGit 3.在VS中设置右键菜单 在菜单栏下方右键,选择自定义 在弹出窗口中选择,命令->上 ...

  10. 【转】基于Jenkins实现持续集成【持续更新中】

    知识预览 持续集成 Jenkins安装 Jenkins插件 Jenkins配置 Jenkins备份与恢复 发布PHP项目 SVN 发布Maven项目 按版本发布 远程管理 War文件部署设置 任务 J ...