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. HDU 3861 The King’s Problem 最小路径覆盖(强连通分量缩点+二分图最大匹配)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3861 最小路径覆盖的一篇博客:https://blog.csdn.net/qq_39627843/ar ...

  2. css实现礼券效果

    <template> <div class="demo"> <div class="stamp stamp01"> < ...

  3. gradle入门教程

    1,https://gradle.org/ 下载过后解压缩,绿色软件不需要安装. 配置系统环境:GRADLE_HOME设置为解压缩之后的地址,PATH属性追加%GRADLE_HOME%\bin; 2, ...

  4. iOS与H5交互(WKWbebView)

    前言: 在iOS开发中,或多或少的会嵌入一些H5页面,有时候需要原生代码和H5页面进行交互.iOS8开始苹果推出性能更强大的WKWebView,所以一下方法是关于WKWebView与JS的交互. 创建 ...

  5. nginx隐藏tp路由index.php

    ---------------------------------------------- 也可以找到vhosts.conf文件修改, 重启nginx,service nginx restart

  6. 以管理员身份运行 cmd 删除无权限删除的文件夹

    rd 文件夹 PS:删除空文件夹 rd /s 文件夹 PS:删除文件夹,及所有子目录和文件 rd /s/q 文件夹 PS:强制删除文件夹,及所有子目录和文件,不需要提示

  7. 24.C# Lambda表达式

    1.Lambda表达式的含义 Lambda表达式是C#3.0引入的一种结构,使用它可以简化C#编程. 2.Lambda表达式与匿名方法 我们知道匿名方法可用于事件处理,如下delegate声明了一个匿 ...

  8. 干货 | LIDAR、ToF相机、双目相机如何科学选择?

    点击"计算机视觉life"关注,置顶更快接收消息! 本文阅读时间约5分钟 本文翻译自卡内基梅隆大学 Chris asteroid 三维视觉技术的选择 传感器参数及定义 LIDAR ...

  9. [TJOI2009]猜数字

    题目描述 现有两组数字,每组k个,第一组中的数字分别为:a1,a2,...,ak表示,第二组中的数字分别用b1,b2,...,bk表示.其中第二组中的数字是两两互素的.求最小的非负整数n,满足对于任意 ...

  10. PL/SQL Block中对单引号进行转义

    可以使用如下的方式: STR := q'[ CREATE TABLE TNAME AS SELECT ... FROM INPUT_TABLE IP WHERE ((IP.DATE_FIELD = T ...