面试: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,然后将新元素放到新数组的最后一个 

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

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值