zoukankan      html  css  js  c++  java
  • 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方法却是使用的是**集合自己**的方法,导致在遍历的时候,会发现某个元素自己神不知鬼不觉地被删除/增加了,这时候,就会抛出一个异常,告诉用户可能会用多个线程对同一个集合发生了并发修改。
  • 相关阅读:
    北京太速科技股份有限公司产品手册V201903020
    204-基于Xilinx Virtex-6 XC6VLX240T 和TI DSP TMS320C6678的信号处理板
    375-基于TI DSP TMS320C6657、XC7K325T的高速数据处理核心板
    116-基于5VLX110T FPGA FMC接口功能验证6U CPCI平台 光纤PCIe卡
    115-基于TI TMS320DM6467无操作系统Camera Link智能图像分析平台
    95-基于FMC接口的2路CameraLink Base输出子卡模块
    88-基于FMC接口的2路CameraLink Base输入子卡模块
    IT男的别样人生,爱折腾,竟然辞职跑丽江去了
    如何扩展/删除swap分区
    Linux 中交换空间 (swap)应该分多大才好?
  • 原文地址:https://www.cnblogs.com/kobelieve/p/10626473.html
Copyright © 2011-2022 走看看