• 阿里巴巴Java规约学习-foreach


    阿里巴巴Java规约学习-foreach

    7. 【强制】不要在 foreach 循环里进行元素的 remove / add 操作。 remove 元素请使用 Iterator
    方式,如果并发操作,需要对 Iterator 对象加锁。
    反例: 
    List<String> a = new ArrayList<String>();
        a.add("1");
        a.add("2");
        for (String temp : a) {
        if("1".equals(temp)){
        a.remove(temp);
        }
    } 
    说明:以上代码的执行结果肯定会出乎大家的意料,那么试一下把“1”换成“2”,会是同样的
    结果吗?
    正例: 
    Iterator<String> it = a.iterator();
        while(it.hasNext()){
            String temp = it.next();
            if(删除元素的条件){
            it.remove();
        }
    } 
    
    先直接看测试结果:
    
    public class Test {
        public static void main(String[] args) {
            List<String> a = new ArrayList<String>();
            a.add("1");
            a.add("2");
    //        for (String temp : a) {
    //            if ("1".equals(temp)) {
    //                a.remove(temp);//正常
    //            }
    //        } 
    //        for (String temp : a) {// java.util.ConcurrentModificationException at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:859) at java.util.ArrayList$Itr.next(ArrayList.java:831)
    //            if ("2".equals(temp)) {
    //                a.remove(temp);
    //            }
    //        }
            Iterator<String> it = a.iterator();
            while (it.hasNext()) {
                String temp = it.next();
                if ("1".equals(temp)) {//不论是1,2都正常
                    it.remove();
                }
            }
            System.out.println(a.toString());
        }
    
    }
    
    

    为什么用foreach的时候,1没问题,2出错了呢?

    先从报错看异常是怎么出来的。
    由报错可知,是next方法中调用checkForComodification出错。
    

    next

    checkForComodification
    所以是modeCount !=expectedModCount导致的。

    foreach是通过Iterable接口在序列中进行移动。
    foreach处打断点,进入可以知道:
    赋值给temp的时候先new Itr()(仅第一次),Itr对象再hasNext()判断,接着再调用next()赋值。

    expectedModCount是初始化(此时生成一个Iterator对象)的时候被赋值的。而modCount又是ArrayList的变量。
    

    expectedModCount

    每次调用list的add/remove的时候,modCount++,而这时它并没有传递给expectedModCount。
    modCount会变化

    当第一次remove掉"2"后,modCount改变 ,for(String temp:a)的时候,会调用ArrayList$Itr.next()方法,next方法会检查modCount是否等于expectedModCount,所以用foreach的时候remove 2出错了。
    

    foreach的时候remove 1没出错呢?

    稍微改下代码:

     List<String> a = new ArrayList<String>();
            a.add("1");
            a.add("2");
            int n=0;
            for (String temp : a) {//正常,但是n为1,说明只有一次
                n++;// 
                System.out.println(n);//只输出1次:1
                if ("1".equals(temp)) {
                    a.remove(temp);
                }
            } 
    

    输出结果:

    1
    [2]

    可见,foreach 只执行了一次循环。
    

    是什么导致本该两次的循环只进入了一次?

    之前说到foreach赋值的时候,会先先用hasNext判断。
    cursor!=size才进入循环体。
    这里的**cursor指的是当前元素的index**。循环体进入一次后,remove掉"1",此时size由2变成了1,cursor也由于next后变为1。再次 for (String temp : a)的时候先调用hasNext因为其相等,所以不再进入循环体。
    

    remove时–size:
    remove时--size
    next后,cursor+1:
    next
    hasNext比较
    hasNext


    为什么直接用Iterator的remove是正常的?

    a.iterator()直接return new Itr(),Itr 实现了Iterator。
    我们看看Itr的remove方法:
    

    Itr的remove方法

    这里明显看出当它remove后, 马上把modCount的值赋值到了expectedModCount上,所以不会出现list.remove那样报ConcurrentModificationException 。
    

    ** 当然如果并发操作,需要对 Iterator 对象加锁。**

    感谢阿里巴巴的Java规约~

  • 相关阅读:
    scanf与scanf_s的区别
    C语言输出时的各种%
    Windows下配置OpenGL环境
    C#高级进阶--重写函数
    Linux下安装国际版QQ (转)
    Linux Vim不明原因卡死解决办法
    iCamera App Kit 使用说明
    usb2.0高速视频采集之68013A寄存器配置说明
    iSensor APP 之 摄像头调试 OV5642 续集2
    iSensor APP 之 摄像头调试 OV9655 测试之二
  • 原文地址:https://www.cnblogs.com/thewindkee/p/12873240.html
Copyright © 2020-2023  润新知