源码解析- java集合迭代器fail-fast/ fail-safe

fail-fast 快速失败   fail-safe 安全失败

说的是在Iterator遍历的过程中,是不能够修改集合数据的,否则就会抛出ConcurrentModificationException。

 

看个demo

执行结果

看一下我们第35行抛了异常 ConcurrentModificationException

追一下看看 ArrayList的第850行

这个方法是通过迭代器获取下一个元素

checkForComodification();  //问题就在这了,获取之前先进行一次判断

就是这了,如果判断成立直接抛得ConcurrentModificationException异常

 

我们看一下modCount和 expectedModCount分别是啥东西

modCount是AbstractList的一个属性,

大概意思就是 当集合的结构发生改变或者size发生变化时,modCount会发生变化

 

看一下ArrayList的几个会改变集合结构的方法 remove()  add() 

 

执行前都会有modCount++操作,也就是说只要集合的结构发生改变,这个modCount就会自增1

 

再看一下expectedModCount,

结了,也就是初始创建迭代器的时候就会expectedModCount = modCount

也就是说 迭代器在遍历的过程中,每次调用next()方法都会重新校验一下,看看这个modCount变没变,也就是有没有人修改了集合的结构,如果有直接抛异常ConcurrentModificationException

 

 

思考一下为什么不允许迭代器遍历的过程中修改集合结构?

大概是因为这边在这遍历查着,那边让人改了 会产生很奇怪的结果吧。

证明一下猜想。首先应从hasNext() next()这两个方法入手, 如何判断下一个元素存在的 

这是ArrayList内部的迭代器实现,看出两个重要的属性,cursor  size

看注释也看得出来,cursor就是游标 是迭代器内部的属性 从0开始遍历,

size是集合整体的大小 是ArrayList的属性,

hasNext()方法判断的就是当前游标和size

next()方法会使得游标+1 然后会记录一下最后一次取出元素的位置 lastRet(也就是刚才游标的位置)

 

那就证明了,如果我们从外部修改了集合的结构,集合的size会发生变化 但是游标cursor不会改,这不就会有问题了吗

 

最后看一下迭代器自带的remove()方法

先判断下modCount  //确保没人傻了吧唧的从外部修改过集合结构

执行remove;      //删的是lastRet位置(也就是迭代器最新一次取出来的元素)

cursor = lastRet;  //游标并没有+1 保持不变

lastRet = -1;     //最新一次取出来的元素已经被干掉了,给-1

expectedModCount = modCount;   //重新对一下expectedModCount

好家伙,不知不觉把迭代器源码就瞅了一遍

 

 

fail-safe

一般的讲 Java.util包下的集合类都是fail-fast的,因为使用迭代器遍历的时候增删元素不安全

那么有不安全就有安全的, java.util.cuncurrent并发包下的集合就很安全

看个demo

 

一模一样的操作,换了copyOnWriteArrayList这个集合就可以了,这是为啥呢?   (程序员的病:  为啥不行呢? 为啥又行呢?)

iterator()  传入  元素数组,0

看出来了  new 的时候保存了一份当前集合元素快照,

即使迭代器遍历的过程中集合发生改变也跟我没关系了,我遍历的始终是迭代器保存的快照 over

 

posted @ 2021-04-07 20:22  六小扛把子  阅读(134)  评论(0编辑  收藏  举报