ArrayList快速失败机制

这篇具有很好参考价值的文章主要介绍了ArrayList快速失败机制。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一、什么是快速失败机制

ArrayList实现了一种称为快速失败(fail-fast)的机制,该机制在并发修改时会抛出ConcurrentModificationException异常。
这种机制的实现原理是:ArrayList在遍历时会记录列表的修改总数(通过modCount字段),如果在遍历过程中列表结构发生变化,那么modCount的值会增大。每次遍历前,迭代器都会检查modCount是否发生改变,如果改变则抛出异常,表示列表并发修改
这个机制的作用是确保在遍历列时候,列表的结构保持稳定,避免并发修改带来未知的结果。

二、例子

        List<String> list = new ArrayList<>();
        list.add("A");
        list.add("B");
        list.add("C");

        for(String s : list){
            if("C".equals(s)){
                list.remove(s);
            }
        }

		System.out.println(list);

ArrayList快速失败机制
因为在遍历过程中调用 list.remove(s) 修改了列表结构,这会导致modCount的值增加,迭代器在下一步遍历前检查到modCount改变,所以抛出异常。

三、底层原理

AbstractList中定义了modCount并初始化为0,modCount是用来记录修改表结构的次数
ArrayList快速失败机制

ArrayList快速失败机制
add操作会将modCount+1
ArrayList快速失败机制
remove方法同样会使modCount++
ArrayList快速失败机制
看到ArrayList中的迭代器的实现,先将expectModCount的值设为modCount的值,在next()方法中最开始会执行一个checkForComodification()方法
ArrayList快速失败机制
这个方法的作用就是检查expectedModCount与modCount是否相等,如果在迭代遍历过程中发生了对list结构的修改操作,modCount的值就不会跟expectedModCount相等,然后抛出异常
ArrayList快速失败机制

二中的例子代码在编译后的结果

        List<String> list = new ArrayList();
        list.add("A");
        list.add("B");
        list.add("C");
        Iterator var2 = list.iterator();

        while(var2.hasNext()) {
            String s = (String)var2.next();
            if ("C".equals(s)) {
                list.remove(s);
            }
        }

        System.out.println(list);

可以看到增强for循环其实本质上就是使用迭代器去遍历

        List<String> list = new ArrayList();
        list.add("A"); // modCount = 1
        list.add("B"); // modCount = 2
        list.add("C"); // modCount = 3
        Iterator var2 = list.iterator(); // expectedModCount = modCount = 3

        while(var2.hasNext()) {
            String s = (String)var2.next(); // 会检查expectedModCount与modCount是否相等
            if ("C".equals(s)) { 
                list.remove(s); // modCount = 4
            }
        }

        System.out.println(list);

这里有人会问,这里明明已经执行到最后一个元素了,会跳出循环,就不会执行next方法,也就不会抛出异常了。看看hasNext()方法的实现
ArrayList快速失败机制
如果没有删除C元素,此时cursor = size = 3
但是因为remove了一个元素,此时数组大小size变成2,而cursor为3,依旧会进入循环去执行next方法,这次执行就会抛出异常。

四、解决方法

要解决这个问题,我们有几种方式:

  1. 遍历结束后再修改列表结构。这是最简单的方式。
  2. 使用迭代器自己的remove()方法。该方法会判断删除元素是否是迭代器当前元素,如果不是则抛出异常,确保安全。
  3. 使用更高级的并发集合,如CopyOnWriteArrayList。该集合在修改时会返回集合的新副本,确保线程安全。
  4. 使用ListIterator,它支持在遍历过程中安全添加和删除。
  5. 调用removeIf()等在遍历时支持删除的方法。

五、快速失败机制的一个小bug

这里使用的是jdk11

        List<String> list = new ArrayList();
        list.add("A");
        list.add("B");
        list.add("C");
        Iterator var2 = list.iterator();

        while(var2.hasNext()) {
            String s = (String)var2.next();
            if ("B".equals(s)) {
                list.remove(s);
            }
        }

        System.out.println(list);

if (“B”.equals(s)) 当我把C改成B后执行代码的结果
ArrayList快速失败机制
可以看到神奇的执行成功了,并没有抛出异常。。。
这里算是一个小bug把,ArrayList如果删除的元素刚好是倒数第二个元素时,不会触发快速失败机制
看到编译后的代码执行过程

        List<String> list = new ArrayList();
        list.add("A"); // modCount = 1
        list.add("B"); // modCount = 2
        list.add("C"); // modCount = 3
        Iterator var2 = list.iterator(); // expectedModCount = modCount = 3

        while(var2.hasNext()) {
            String s = (String)var2.next(); // 会检查expectedModCount与modCount是否相等
            if ("B".equals(s)) { 
                list.remove(s); // modCount = 4
            }
        }

        System.out.println(list);

在执行完移除B的操作后,此时数组变成了 [A,C],数组size变成了2,又因为在执行完next方法之后会将迭代器的下标cursor+1指向下一个元素,此时cursor也为2,然后在hasNext方法里判断就会为false,直接跳出了循环(没有执行会抛异常的那个next方法),导致这次代码执行是成功的。文章来源地址https://www.toymoban.com/news/detail-440649.html

到了这里,关于ArrayList快速失败机制的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Java 集合中 ArrayList 的扩容机制原理(面试+读源码)

               在 Java 中,ArrayList 内部是通过一个数组来存储元素的,是一个数组结构的存储容器。当向一个 ArrayList 中添加元素时,如果当前数组已经满了,就需要扩容。          集合的继承关系图  ( ArrayList 的扩容机制原理 )          面试官好,ArrayList 是一个数

    2024年02月07日
    浏览(34)
  • 深入源码解析ArrayList:探秘Java动态数组的机制与性能

    1.1 介绍ArrayList的基本概念和作用 在Java中,ArrayList是一个实现了List接口的动态数组。它可以根据需要自动增加大小,因此可以存储任意数量的元素。 基本概念: ArrayList是Java中常用的集合类之一,它可以存储对象,并且可以根据索引访问和操作这些对象。 ArrayList是基于数组

    2024年02月04日
    浏览(31)
  • 学习一下Java的ArrayList和contains函数和扩容机制

    在Leetcode上做题写了两种暴力解法,但是执行效率上不太一样。 时间上差很远,内存虽然差不多但是前者击败30%,后者击败94%。这两种解法区别是用一条 ArrayList 还是两条来存数据,所以contains虽然执行次数一样但是检测的长度上不一样,而且 ArrayList 的扩容次数也不一样,所

    2024年02月08日
    浏览(39)
  • java面试基础 -- ArrayList 和 LinkedList有什么区别, ArrayList和Vector呢?

    目录 基本介绍 有什么不同?? ArrayList的扩容机制 ArrayLIst的基本使用 ArrayList和Vector 还记得我们的java集合框架吗, 我们来复习一下, 如图:          可以看出来 ArrayList和LinkedList 都是具体类, 他们都是接口List的实现类. 但是他们底层的逻辑是不同的, 相信学过这个的应该大概有个

    2024年02月12日
    浏览(30)
  • 为什么arrayList线程不安全?

            ArrayList是Java中的一种动态数组,它在内部使用数组来存储元素。ArrayList的线程不安全性主要体现在多线程并发访问和修改同一个ArrayList实例时可能出现的问题。         当多个线程同时对ArrayList进行修改操作时,可能会导致数据不一致或者出现异常。这是因为

    2024年02月12日
    浏览(40)
  • ArrayList 和 LinkedList 有什么区别

    ArrayList 和 LinkedList 是Java中的两种常见的集合类型,它们具有一些相似之处,但也存在一些重要的区别。 ArrayList ArrayList 是数组列表类型,它是通过一个可变大小的数组来实现的。这意味着 ArrayList 支持随机访问元素,因为每个元素都可以通过索引来访问,而且访问速度很快。

    2024年02月05日
    浏览(30)
  • java面试基础 -- ArrayList 和 LinkedList有什么区别

    目录 基本介绍 有什么不同?? ArrayList的扩容机制 ArrayLIst的基本使用 ArrayList和Vector 还记得我们的java集合框架吗, 我们来复习一下, 如图:          可以看出来 ArrayList和LinkedList 都是具体类, 他们都是接口List的实现类. 但是他们底层的逻辑是不同的, 相信学过这个的应该大概有个

    2024年02月12日
    浏览(39)
  • ArrayList为什么不是线程安全的,如何保证线程安全?

    官方曰, 线程安全就是多线程访问时,采⽤了加锁机制,当⼀个线程访问该类的某个数据时,进⾏保护,其他线程不能进⾏访问直到该线程读取完,其他线程才可使⽤。不会出现数据不⼀致或者数据污染。线程不安全就是不提供数据访问保护,有可能出现多个线程先后更改数

    2024年02月07日
    浏览(50)
  • 每日一道面试题之ArrayList 和 LinkedList 的区别是什么?

    ArrayList 和 LinkedList 是Java中常用的两种集合类,它们在实现和使用上有一些区别,如下所示: 内部实现 : ArrayList 是 基于数组实现 的动态数组,而 LinkedList 是 基于双向链表 实现的。 插入和删除操作 : ArrayList 在插入和删除元素时,需要移动其他元素来保持其数组元素位置

    2024年02月16日
    浏览(31)
  • 面试官问 : ArrayList 不是线程安全的,为什么 ?(看完这篇,以后反问面试官)

    金三银四 ? 也许,但是。 近日,又收到金三银四一线作战小队成员反馈的战况 : 我不管你从哪里看的面经,但是我不允许你看到我这篇文章之后,还不清楚这个面试问题。 本篇内容预告:   ArrayList 是线程不安全的, 为什么 ? ① 结合代码去探一探所谓的不安全  ② 我们

    2024年02月02日
    浏览(47)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包