• ArrayList之foreach循环删除倒数第二个元素,不触发fail-fast机制


    今天一朋友问了个问题,对于如下一段代码,运行后会有怎样的结果?

    public class ArrayListTest {
    
        public static void main(String[] args) {
            List<Integer> list = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5));
            for (Integer num : list) {
                if(num == 4){
                    list.remove(num);
                }
            }
            System.out.println(list);
        }
    
    }

    由于本人之前对于集合框架中的fail-fast机制有过一些了解,所以看到这种写法,很自然的认为会出现java.util.ConcurrentModificationException异常,控制台并不会输出预期的结果。

    但事实情况呢?看如下截图,执行成功了。

    本人又尝试将if判断中num依次换成1,2,3,结果和预期一样了。

    到底是什么原因呢,只有删除倒数第二个元素的场景结果和预期不一样。

    本人带着好奇心、调试了一把源码,终于发现了庐山真面目。

    类似于以上的forEach循环,其实是一种语法糖,调试后就会发现,list在循环时是由Iterator迭代器来实现的。

    所以真实的代码则类似如下:

    public class ArrayListTest {
    
        public static void main(String[] args) {
            List<Integer> list = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5));
            for (Iterator<Integer> itr = list.iterator(); itr.hasNext();){
                int num = itr.next();
                if(num == 4){
                    list.remove(num);
                }
            }
            System.out.println(list);
        }
    
    }

    调试后终于发现了玄机,先看下面三个迭代器的关键属性:

    cursor --迭代器的游标,元素的索引值,初始值为0

    lastRet --返回最后一个元素的索引值、如果没有找到则返回-1

    expectedModCount --修改次数的期望值,可以看到在迭代器初始化时,这个属性就被赋值为当前修改次数的值了。

    checkForComodification --此方法用来检查修改次数是否发生变化,从而判断是否需要触发fail-fast异常。

    在迭代过程中,每一次迭代,cursor会+1, 而itr.hasNext()会判断是否存在下一个元素、irt.next()获取下一个元素的值,最终直到不存在下一个元素,则迭代结束。

    跟进源码发现,itr.hasNext()判断方法并不会调用checkForComodification方法来检查list在迭代中是否有被修改,只是判断游标和长度是否相等,不等时则认为存在下一个元素。

    在删除倒数第二个元素的场景下:当倒数第二个元素时迭代完成,开始迭代最后一个元素时,此时cursor是4,size由于在迭代过程倒数第二个元素移除了,所以-1, 此时cursor和size相等,不会再进入下一个迭代,因此不会触发checkForComodification方法的fail-fast机制。

    其他三种情况,再删除元素后的下一个迭代,由于会调用it.next()方法,则会触发ConcurrentModificationException异常。

     

    通过这次对arraylist中iterator迭代器源码的深入学习,本人对于迭代器的运作机制又有了进一步的认知。后续还要再深入学习jdk中的集合框架源码,不能过于自信~

  • 相关阅读:
    负载均衡获得真实源IP的6种方法
    美图全链路监控实战
    移动端APM网络监控与优化方案
    k8s 如何对外提供服务
    mysql5.7安装audit审计插件
    mysql 5.7安装密码校验插件validate_password
    Linux Crontab 定时任务
    stm32 hard fault usage fault UNALIGNED -> task stack overflow
    linux逻辑卷管理(LVM)
    suse11开启telnet服务
  • 原文地址:https://www.cnblogs.com/carryjack/p/8018992.html
Copyright © 2020-2023  润新知