foreach循环里不能remove/add元素的原理

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方法却是使用的是集合自己的方法,导致在遍历的时候,会发现某个元素自己神不知鬼不觉地被删除/增加了,这时候,就会抛出一个异常,告诉用户可能会用多个线程对同一个集合发生了并发修改。

原文地址:https://www.cnblogs.com/kobelieve/p/10626473.html

时间: 2024-08-26 02:12:32

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

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

在阅读<阿里巴巴Java开发手册>时,发现有一条关于在 foreach 循环里进行元素的 remove/add 操作的规约,具体内容如下: 错误演示 我们首先在 IDEA 中编写一个在 foreach 循环里进行 remove 操作的代码: import java.util.ArrayList; import java.util.List; public class ForEachTest { public static void main(String[] args) { List<S

不要在foreach循环里进行元素的remove/add操作

不要在 foreach 循环里进行元素的 remove/add 操作.remove 元素请使用 Iterator 方式. 反例: public class ForeachTest { private List<String> list = new ArrayList<String>(); @Test public void forTest() { list.add("1"); list.add("2"); for(String temp :

在foreach循环中使用remove报ConcurrentModificationException异常原因

在foreach循环中使用remove报ConcurrentModificationException异常原因 我的代码具体是这样的 int dindex=0; int did=getInt("请输入需要删除的学生学号:"); for (Student student:list) { if(student.id==did){ list.remove(student); } } 这样会导致remove后,导致list在循环中下标和实际已经被修改后的下标不一致 我自己的解决方案是: int

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

在阿里巴巴Java开发手册中,有这样一条规定:但是手册中并没有给出具体原因,本文就来深入分析一下该规定背后的思考.1.foreach循环foreach循环(Foreach loop)是计算机编程语言中的一种控制流程语句,通常用来循环遍历数组或集合中的元素.Java语言从JDK 1.5.0开始引入foreach循环.在遍历数组.集合方面,foreach为开发人员提供了极大的方便.通常也被称之为增强for循环.foreach 语法格式如下: for(元素类型t 元素变量x : 遍历对象obj){ 引

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

这个问题我在实际写代码中也遇到过,当时还很疑惑,刚看到这里有一些解释得挺清楚,记录一下: 原文地址为:https://mp.weixin.qq.com/s?__biz=MzI3NzE0NjcwMg==&mid=2650123395&idx=1&sn=b089eac4f561f58ee8a92602db17f577&chksm=f36bb1a2c41c38b4f36c1a84f205e188a6a8aa2684f316e4dcb8d1162b6e94b970c670b2e5b

为什么在foreach循环中不要对元素进行remove/add操作?

目录 前言 正文 为什么结果如此不同? remove/add方法? 如何解决? 前言 在阿里巴巴Java开发手册中,有下面这样的规定: 这篇文章我们就来深入探讨其中的原因. 正文 为什么结果如此不同? 我们先来看看前言中的反例会出现什么意料之外的结果: ---------------------------------------------------------------------------------------------------------------------------

跟王老师学集合(四):使用foreach循环遍历元素

使用foreach循环遍历元素 主讲人:王少华  QQ群号:483773664 学习目标: 1.掌握使用foreach循环遍历元素 JDK1.5及以后的版本,可以通过foreach来迭代访问集合元素. 一.使用foreach来遍历狗狗集合 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 public class Test {     public static void main(String[] args) {         

Java使用foreach遍历集和时不能add/remove的原因剖析

foreach 与 Iterator 我们知道,在Java中使用foreach对集和进行遍历时,是无法对该集和进行插入.删除等操作,比如以下代码: for(Person p : personList){ if(StringUtil.isBlank(p.getName())){ personList.remove(p); } } 执行代码,报以下异常: Exception in thread "main" java.util.ConcurrentModificationException

C#不允许在foreach循环中改变数组或集合中元素的值(注:成员的值不受影响)

C#不允许在foreach循环中改变数组或集合中元素的值(注:成员的值不受影响),如以下代码将无法通过编译. foreach (int x in myArray) { x++; //错误代码,因为改变了元素的值 Console.WriteLine(x); } 如果要让自定义的数据类型支持foreach循环,则该类型必须实现IEnumerable<T>接口,且存在对应此列表的IEnumerator<T>实现. 实际上,在.Net的底层(IL语言层面)而言, foreach (var