• java 迭代器 Iterator


    迭代器的作用就是用来遍历集合的

    那么,在遍历的过程中,是否允许其他人来修改集合?

    有两种处理策略:Fail-Fast 与 Fail-Safe

    1.Fail-Fast

    fail-fast 一旦发现遍历的同时其它人来修改,则立刻抛异常

    ArrayList 是 fail-fast 的典型代表,遍历的同时不能修改,尽快失败

    测试代码

    创建一个ArrayList集合并添加元素,遍历输出

    添加断点,在遍历至 C 时,可以debug,模拟另一个线程,修改list,添加一个元素

    查看 list 集合,由4个元素,变成了5个元素

    放开断点,将 C 遍历输出后,下一轮循环时,报错(并发修改异常) java.util.ConcurrentModificationException ,

    原理:

    for循环会用到底层的迭代器,首次循环的时候,会创建一个迭代器对象

    创建一个迭代器对象时,初始化迭代器的一些成员变量

    modCount是 list数组 集合的成员变量,记录 list数组 被修改了几次(现在数值是4,因为add了4次)

    expectedModCount 是迭代器的成员变量,记录迭代初始时,list数组 修改的次数 

    接下来会调用迭代器的hasNext()、next()方法,向下移动,每次调用next()方法时,会调用checkForComodification(),对 expectedModCount 做检查

    当发现,迭代开始时的list数组修改次数(4),与list数组修改次数(5)不一致时,抛出异常

    即,通过判断循环开始时list数组的修改次数,与循环中的list数组的修改次数是否相等,来判断循环过程中,list数组是否发生修改

    如何查看list数组的modCount?

    idea控制台,默认显示list数组的元素,进行一下修改

     将List选项,改为Object选项,就能看到一些数的成员变量

     2.Fail-Safe

    fail-safe 发现遍历的同时其它人来修改,应当能有应对策略,例如牺牲一致性来让整个遍历运行完成

    CopyOnWriteArrayList 是 fail-safe 的典型代表,遍历的同时可以修改,原理是读写分离

    测试代码

    创建一个CopyOnWriteArrayList集合并添加元素,遍历输出

    添加断点,在遍历至 C 时,可以debug,模拟另一个线程,修改list,添加一个元素,查看 list 集合,由4个元素,变成了5个元素

    放开全部断点,遍历输出了4个,但集合有5个元素

    虽然没有报错,但是牺牲了一致性

    原理:

    在首次循环,会创建迭代器对象,不过对象变成了COWIterator

    进入构造器的构造方法,会把当前正在遍历的数组记录下来,保存在snapshot成员变量中

     依然创建集合,debug,在循环至C时,添加元素,发现list数组的元素变成了5个

    进入迭代器看看,发现是初始的4个元素

    为什么呢?

    需要看下add的源码

    注意这一句,add时,将原数组复制了一份,生成一个新数组,并长度+1,然后将新元素放到新数组的最后一个 

    每次添加新元素时,都会复制出一个新数组,添加结束后,就会把初始数组替换成新数组,但在遍历过程中,使用的是初始数组

    添加是一个数组,遍历时另一数组,二者互补干扰

  • 相关阅读:
    基于STM32设计的智慧农业管理系统(ESP8266+腾讯云微信小程序)
    node开发微信群聊机器人第④章
    Qt全屏显示与退出
    PyCharm中文使用详解
    IDEA代码重构技巧--抽取+内联
    centos7安装keepalived 保证Nginx的高可用
    FFmpeg源代码简单分析-其他-libavdevice的gdigrab
    信息学奥赛一本通:陶陶把手伸直的时候能够达到的最大高度
    如何入门 AI:从初学者到人工智能专家的完整指南
    MICCAI 2022:使用自适应条形采样和双分支 Transformer 的 DA-Net
  • 原文地址:https://blog.csdn.net/hfaflanf/article/details/126051578