分享一个有意思的错误

subList方法拆分集合问题

JAVA技术交流群:737698533

分享一个有意思的错误,先看代码

 public static void main(String[] args) throws IllegalAccessException {

        ArrayList list = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            list.add(i);
        }

        List aList = list.subList(0, 2);
        List bList = list.subList(2, 4);

        ArrayList cList = new ArrayList<>();
        cList.add(1);

        aList.addAll(cList);

        for (Integer i : bList) {
            System.out.println(i);
        }
 }

逻辑很简单,将一个有10个元素的集合拆分为两个集合 aLisbList,然后创建一个新的集合 cList,添加一个数据,之后调用addAll方法,将 cList添加到 aList中,最后遍历 bList

看着代码没啥问题吧,运行:

分享一个有意思的错误

这个是啥错呢?网上搜了一下大部分都是说在循环中不能对list集合进行修改,但是上面的代码中并没有在循环中修改啊???很迷惑

要想搞明白这个问题先来看看for循环的本质是什么

分享一个有意思的错误

写一段for循环的代码.使用idea插件 jclasslib可以看到,在代码中使用的for循环,而编译器给你编译为字节码后其实是一个迭代器

那么直接写成迭代器的形式方便下面的观察,将上面的代码最后一段for循环改为

    Iterator iterator = bList.iterator();
    while (iterator.hasNext()) {
        System.out.println(iterator.next());
    }

从list的subList方法入手

public List subList(int fromIndex, int toIndex) {
    subListRangeCheck(fromIndex, toIndex, size);
    return new SubList(this, 0, fromIndex, toIndex);
}

能看到如果使用了subList进行拆分,那么给你返回的不是一个创建时使用的ArrayList了,而是返回了一个SubList,可以通过反射来获取类名证明返回的是一个subList类

分享一个有意思的错误

继续查看SubList这个类

分享一个有意思的错误

发现它是一个ArrayList的内部类,同ArrayList都继承了AbstractList类

注意这个SubList的有参构造最后一行,在调用subList方法后,就将当前List的modCount值赋值给了SubList类

那么现在有几个问题:啥时候报的错,为什么报错,在哪报的错,我们直接debug看

分享一个有意思的错误

当走完addAll()方法后idea已经开始提示会出现bug,当我们继续走完bList.iterator()方法后,程序出错,然后退出

分享一个有意思的错误

也就是说在调用iterator()方法后,出现的错误,我们继续debug进入查看

分享一个有意思的错误
分享一个有意思的错误
分享一个有意思的错误
分享一个有意思的错误
分享一个有意思的错误

到最后发现ArrayList的modCount和SubList类中的modCount判断不同,所以才抛出的错误

那modCount是干啥的?简单来讲就是记录当前集合被更改的次数

上面的三个问题已经解决了

啥时候报的错:当调用iterator()方法时

为什么报错:ArrayList的modCount和SubList类中的modCount值不同

在哪报的错:bList的iterator()方法里

那么现在又有新的问题:ArrayList的modCount值什么时候改的?为什么对aList进行addAll操作,循环bList会出错?

debug看看addAll()方法

分享一个有意思的错误

分享一个有意思的错误

分享一个有意思的错误

而修改的这个属性是在AbstractList当中的

分享一个有意思的错误

那这两个subList又不是同一个对象,咋能共用ArrayList中的modCount呢?

分享一个有意思的错误

也确实不是同一个对象,但是这个两个对象都是使用同一个List创建出来的,而他俩都是内部类

在创建subList时都有传入过一个parent参数,传入的参数都是this

分享一个有意思的错误

我们直接看看这两个subList类中的parent属性是否一样即可

分享一个有意思的错误

因为List重写了toString方法,无法通过toString看到地址,所以通过hashCode也可以来(大致)判断是否是同一个对象

那么上面两个问题也解决了

ArrayList的modCount值什么时候改的: 当调用addAll方法时进行修改的

为什么对aList进行addAll操作,循环bList会出错: 因为外部类是同一个,修改的modCount是同一个,都在AbstractList当中,当循环bList实际上就是使用迭代器,调用iterator时会判断ArrayList的modCount和当前的modCout,因为aList调用addAll方法导致AbstractList当中的modCount值进行了改变,因为aList和bList是同一个List创建出来的,他们的外部类是一样的,那么bList判断时就会出错

越是不符合逻辑的地方,越埋藏着更深刻的逻辑

Original: https://www.cnblogs.com/sunankang/p/15031760.html
Author: Jame!
Title: 分享一个有意思的错误

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/622044/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球