粉丝2060获赞1.8万


哈喽,大家好,我是玉峰,欢迎大家收看本期视频啊,那么今天呢,我们一起来看一下这个 list item remove 啊,它里面会产生了一些问题, 然后呢我们会去深入源码研究一下他为什么会产生这样的一个问题,并且会给出几个推荐的做法啊。首先这个地方是一个准备的数据啊,通常这个问题呢,是由于 concurrent modification exception 这个异常导致的啊,比如说,首先我们来运行一下这个 test, 一 这个地方就爆异常了,对吧?我们来看一下他这个代码是怎么写的。首先呢,有一个 list 数据,然后我们循环这个 list 数据啊,但是这个地方用的是 gdk 一点五之后的这个高级的复循环,然后在复循环的内部呢,我们会有一个条件的判断, 如果满足某个条件,我们就把对应的元素给他删除,那报错呢?具体是在这一行,对吧?啊?我们来到他上一行,也就是 next 这个地方来看一, 为什么是 next 呢啊?首先我们来看一下这个 next, 它属于什么类啊?它这个地方是一个 it 二啊,这个 it 二呢,其实是 a release 内部的一个迭代器的实现啊,我们可以看到它,呃,实现的是这个 iterator 接口 next 方法就是 获取下一个元素,对吧?但是在获取下一个元素的时候呢,他首先会去教验一下哎。呃, mode count 是不是等于这个 expected mode count? 我们来看一下这个 mode count 它是什么东西啊?这个地方它的注释说的很明确啊,这个 list 啊,从创建以来到现在啊,被结构化修改的次数。 然后我们再来看一下这个 expected mode count 啊,这个 expected mode count 当你再去出示这个 itr 对象的时候呢,它是和外部的这个节油化修改的这个次数是一样的啊,但是我们现在抛出了这个异常,就代表外部的这个修改次数和当 当前迭代器内部的这个修改次数,他俩是不一样的啊,因为这个地方这个方法叫什么 check for co modification 啊,就是内外两侧的这个修改,哈哈,他们是不是一致啊?这个其实是一个 异常的前置抛出啊,因为他抛出是在那个 next 方法,他没有在具体的这个写的方法,而是一个读的方法啊,所以说他是一个元素内部现成安全的一个保护措施啊。那到底是什么原因导致内外的这个修改次数不一致呢?其实是这个 listing remove 方法 ok 啊,我们来到这个 released, 我们可以来看一下他的这个 remove 实现啊,其实具体内部是调的这个 fast remove 方法啊,啊,就是外部的这个修改次数 mode count 会去加一啊,但是他并没有任何去操作,内部就是那个 itr, 那个叠带器类内部的那个 expected mode count, 他没有去做任何的操作, 所以这个就导致了内外的啊修改次数不一致啊,是这个原因,对吧?那很多同学在出现了这个看 current modification except 的时候呢,就会去网上查资料啊,通常网上的一些不太好的博客会告诉你啊,使用这种方式啊, 但是使用这种方式我发现好像没有什么区别啊,因为你用的是这个 fore 气啊,我们可以来看一下这个 fore 气他具体的一个实现啊, 其实这个 forease 它用的也是高级 fore 啊,这个地方有一个 zees 啊,表明谁来调用就循环谁,然后它产生的每一个元素呢,会基于你传入的这个拉姆达表达式去对它进行一个逻辑的处理啊,就这段 就是你传入的那个 number 表达式,所以说这个他也是会爆错的啊,跟上面那个是没有任何区别的。然后呢,还有一些博客,他其实写的也不是很好啊, 把这个问题转移了啊,你高级 four 不能这么删啊,你要用这个低级 four 啊,那么我们来看一下这个低级 four 会不会产生同样的这个问题啊?哎,好像没有产生这个问题啊,那有的同学可能就觉得看 current motivation exception 是不是就解决了啊,但是我们会发现这个好像 太符合预期啊,就是比如说这个哔哩哔哩,他也是以 b 开头的,但是没有被删除掉啊。其实我觉得很多同学已经想到什么原因了,就是这个 listen size 他是不断在变化的,于是他又懂了哈,他把这个 listen size 提取出来啊, 但是呢,他会产生另外一个问题,就是角标越界啊,这个塞子虽然是不变了,但是他后面的这个 i 是不断在累加的啊,所以说他会造成这种角标越界啊。于是这哥们实在没有办法了,他就上网寻找了答案,就是 list 在删除的时候怎么样避免这个角标越界, 怎么样就是各种查询对吧?然后他发现啊,有些比较聪明,比较讨巧的这种程序员呢,他会想到了,就是作为一个逆序的删除,确实这种逆序的删除是没有任何问题的,然后他也实现了这个 啊,逻辑就是把数据删的很干净啊,我们可以运行来看一下,的确是所有 b 开头的全部都被删掉了,对吧?啊?那其实比较正确的做法就是我一开始说的啊,调用 list 内部的那个 activator 来进行一个迭代来进行一个操作,对吧?所以说我们只需要调用这个 atriter 他自己提供的这个 remove 方法就可以了,对不对啊?比如说我们这个地方去到这个 array list 啊,然后看一下他的这个 itr 这个地方有一个 iterator 方法,对吧?刚才我们调用它,实际上它是六了一个 it 二,然后这个 it 二呢,它内部有一个 next 方 方法,然后他自己内部呢?还有一个 remove 啊,我们一起来看一下他 remove 的一个实现啊,主要是看这个 try cash 这一块,首先第一句话就是他会调用外部的那个 remove 方法去移除掉这个元素, 移除掉了以后呢,他会做这样的一个操作,就是把内部和外部的这个操作次数打平一下,让他们两个相等。所以说你之后再去调这个 next 获取下一个元素的时候呢,就不会爆啊,咱们之前那种错误了。 ok, 这个是比较推荐的一个做法。 ok 啊,但是这个写法 有些同学还是会犯错误啊,就是他五十,五十吧,我们来看一下这个 test 五杠一哎,他循环的时候呢,包括他,呃,这个获取元素都是用的这个 h waiter, 但是他最后再删除的时候呢,还是用的这个 list 点尾部法,这种方式同样 会报那个 exception 啊,看 current modification exception 啊,这个,所以说大家一定要注意啊,不要啊,一百分的卷子得了九十九分啊,最后就败在了这一个很小的细节, ok, 好,那最后呢,其实还有另外一个方式就是使用这个 filter 过滤啊,我们知道就是你在删除 list 当中某些元素的时候呢,其实你是想得到它 啊,反向条件的一些元素,对吧,所以说我们直接用这个 filter 就可以了,对吧?好的,那么以上呢,就是本期视频的一个全部内容了,如果大家觉得这个视频做的还不错的话呢,请帮忙点赞,投币分享,一键三连,支持我一下, ok, 我们下个视频再见吧! peace。

哈喽,昨天跟大家分享了我们如何用 list 去删除里面的元素,然后呢就有小伙伴问,为什么我们的增强或循环会出现并发修改异常,而我们的迭代器却不会出现呢?今天呢,我们就来看一下底层的一个原理啊。首先我们运行一下, 然后呢我们就看到了这样的一个并发修改异常,对吧?然后呢我们去追溯这样的一个元代码,点进去这边呢出现了一个。 net 方法,那这个 net 方法呢?在我们 itr 这个内部类里面, itr 这个类呢是我们 released 迭代器的一个实现,我们可以看一下 他这边呢是六了一个 i t r, 然后返回这样的一个叠带器,那增强后循环其实就是用到了底层叠带器的一个便利啊。那 i t r 里面呢,有一个 hard next 方法和 next 方法,很明显我们肯定是先要 去判断有没有下一个元素,有的话我们才会获取下一个元素,对吧?啊,当我们在调用 next 方法的时候呢,它底层会调用这样的一个方法叫 check for commodification, 然后我们点进去看一下, 这个里面呢,他会判断两个值是否相等,如果不相等的话,我们会抛一个叫并发修改异常。那茉莉康的这样的一个属性呢?他是我们 release 的一个成员变量值,他标记的呢就是我们 release 的修改的一个次数。 那 expected mode count 的这个值呢?它是我们 r e t r 内部的一个属性,那在初始化的时候,我们就会把我们 mode count 的的一个值给我们 expected mode count 值保持一个同步。然后呢我们看一下 list 点 remove 的一个方法,我们点进去看一下,看一下 released 的一个实现类, 我们可以看到在这个方法里面呢,其实主要就是一个 faster move, 我们可以点进去看一下,那在这个 方法里面呢,我们对 mod count 的一个值进行加加,但是呢我们没有看到任何关于对 expected mod count 的一个值进行修改,那他在下一次便利的时候呢,就会调用我们迭代器的一个 next 方法,然后呢就会走到这边, 然后去检查两个值是否相等,而这边值已经被修改过了,所以呢就会抛这样的一个异常。那我们的迭代器便利为什么就没有出现这样的异常呢? 那在叠带器的乳木方法里面啊,他把这两个值呢进行了同步,所以呢就不会出现那个异常。那如果说大家想要这样的一个 demo 以及之前的 demo 的话呢,可以到这来这,然后给他去发一个消息,像这样就可以拿到我们的 demo。 好,今天的一个分享呢,就到这。


哈喽,今天给大家分享一下我们如何删除 list 中的元素。那比如我这边有一个 list, 我们要删除里面姓李开头的人,比如我们可以使用普通的否循环删除,我们运行一下, 运行之后发现啊,我们的李白呢?他还有,那为什么呢?是因为我们 list decide 和我们爱的值一直在动态变化,爱的值呢,一直在累加, list decide 的值一直在减少, 所以类似的就会早早的结束了,循环他是不可靠的。再来看一个普通否循环提取变量删除,也就是我们把这样的一个类似类赛的值呢给他提取出去执行一下,这边呢就直接抛出了下标越界异常。这里呢,也很明显,我们 们 size 的值是固定的,而 list 的一个实际大小它是不断减少的, i 的值它是不断累加的,一旦 i 的值大于了 list 的一个实际大小,肯定就抛出异常了。 我们再来看一个普通货循环道序删除,我们来运行一下,那他的一个结果呢,是输出正常的,那为什么呢?是因为绿色联赛的值呢?他只用到了一次。我们再来看一个增强货循环的删除,那这种呢,也会抛异常,只是呢,他会抛一个并发修改异常, 那并发修改异常呢,是集合中非常常见的一个异常之一。那我们再来看一个迭代器循环删除,那我相信很多人呢,都会用这样的一个删除的方法,这种呢是可以正常输出的,是安全的。我们再来看一 个迭代器循环删除,我们虽然用到了迭代器,但是它的里面呢,仍然是用类似的点规目,所以呢,他依然会有并发修改异常出现,我们再来看一个集合类似的碘伏一次删除。这个呢,依然会出现并发修改异常, 我们点进去看一下啊,他底层呢,其实就是一个增强否循环,底层就是一个叠带器,所以使用 list 点 remove 呢,他依然会有并发修改异常。那最后一个呢?当然是使用我们 stream 的 filter 过滤方法, 这个方法呢利用了 stream 的一个筛选功能,快速过滤所需要的元素,同样能达到我们的一个效果,所以比较推荐。那如果说大家想要这样的一个 demo 以及之前的 demo 的话呢,可以到这来 这,然后给他去发一个消息,像这样就可以拿到我们的 demo。 好,今天的一个分享呢,就到这。

在 java 中,有几种方法可以便利 list 并动态删除元素。以下是一些常见的方法,一、使用 four h 循环推荐,这是最简单且最常见的方法。 你可以使用 free 一循环来便利类似并在需要的时候删除元素。这种方式最大的优点是简单易用,但也有一些缺点,比如如果列表很大 可能会导致性能问题。二、使用增强富尔循环推荐,在扎俄舞之后还可以使用增强富尔循环,也称为 footage 循环来便利类似, 并在需要的时候删除元素。这种方式和 forage 循环类似,但是更加简洁,但是和 forage 循环一样,如果列表很 大可能会影响性能。三、使用 iterator 你也可以使用 iterator 来便利类似并删除元素。这种方式的好处是可以按需删除, 而不是在列表中间进行。但是他的缺点是代码更复杂,并且在某些情况下可能会导致数据不一致。四、使用 java 八的流 stream 扎巴巴引入了刘 stream 的概念,可以很方便的处理集合。但是要注意的是,使用刘进行便利并删除元素并不是最佳实践,因为刘本身并不是设计用来修改集合的。 如果需要修改集合,最好使用其他方法。但是如果你只是想展示流的使用方法,以下是一个例子。总结,以上几种方法都 都有其优缺点,你可以根据自己的需求和场景选择合适的方法。一般来说,推荐使用 forage 循环或增强 for 循环来便利并删除类似中的元素。如果列表很大可能需要考虑性能问题。 对于更复杂的需求或性能要求更高的场景,可以考虑使用 iterat 或扎巴巴的流。
