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

Posted kobelieve

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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方法却是使用的是集合自己的方法,导致在遍历的时候,会发现某个元素自己神不知鬼不觉地被删除/增加了,这时候,就会抛出一个异常,告诉用户可能会用多个线程对同一个集合发生了并发修改。

以上是关于foreach循环里不能remove/add元素的原理的主要内容,如果未能解决你的问题,请参考以下文章

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

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

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

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

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

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