subList方法拆分集合问题

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

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

        ArrayList<Integer> list = new ArrayList<>();
for (int i = 0; i < 10; i++) {
list.add(i);
} List<Integer> aList = list.subList(0, 2);
List<Integer> bList = list.subList(2, 4); ArrayList<Integer> 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<Integer> iterator = bList.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}

从list的subList方法入手

public List<E> 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又不是同一个对象,咋能共用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判断时就会出错

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

subList方法拆分集合问题的更多相关文章

  1. List集合数据太多进行分批,List的subList方法应用

    List<String> mStrings=new ArrayList<>(); //初始化 for (int i = 0; i < 1020; i++) { mStri ...

  2. java List.subList方法中的超级大陷阱

    ArrayList 中 subList 的基本用法: subList(fromIndex:int,toIndex:int):List<E> 返回从fromIndex到toindex-1 的 ...

  3. 使用java.util.List的subList方法进行分页

    java.util.List中有一个subList方法,用来返回一个list的一部分视图. List<E> subList(int fromIndex, int toIndex); 它返回 ...

  4. 为什么阿里巴巴要求谨慎使用ArrayList中的subList方法

    GitHub 3.7k Star 的Java工程师成神之路 ,不来了解一下吗? GitHub 3.7k Star 的Java工程师成神之路 ,真的不来了解一下吗? GitHub 3.7k Star 的 ...

  5. 谨慎使用ArrayList中的subList方法

    转自:https://www.toutiao.com/a6705958780460335619/?tt_from=weixin&utm_campaign=client_share&wx ...

  6. ArrayList.subList方法使用总结

    ArrayList.subList方法使用总结 示例 List<String> list=new ArrayList<>(); list.add("d"); ...

  7. JVM-class文件完全解析-方法表集合

    方法表集合 前面的魔数,次版本号,主板本号,常量池入口,常量池,访问标志,类索引,父类索引,接口索引集合,字段表集合,那么再接下来就是方法表了.   方法表的构造如同字段表一样,依次包括了访问标志(a ...

  8. 需要注意的subList方法!和substring是不一样的!从源码解释他们的不同。

    很多时候我们截取字符串用的是substring方法,很自然用着,但是对于列表的截取时很多时候就用得很少,但是其实他们是很不一样的,具体哪里不一样呢? package main; import java ...

  9. js/jq基础(日常整理记录)-2-一个简单的js方法实现集合的非引用拷贝

    一.一个简单的js方法实现集合拷贝 做web项目的时候,少不了和js中的数组,集合等对象接触,那么你肯定会发现,在js中存在一个怪异的现象就是数组和集合的拷贝都是地址复制,并不是简单的数据的拷贝. 举 ...

随机推荐

  1. Jittor框架API

    Jittor框架API 这里是Jittor主模块的API文档,可以通过import jittor来获取该模块. classjittor.ExitHooks exc_handler(exc_type,  ...

  2. 旷视MegEngine网络搭建

    旷视MegEngine网络搭建 在 基本概念 中,介绍了计算图.张量和算子,神经网络可以看成一个计算图.在 MegEngine 中,按照计算图的拓扑结构,将张量和算子连接起来,即可完成对网络的搭建.M ...

  3. 蓝牙mesh网络技术的亮点

    蓝牙mesh网络技术的亮点 The highlights of Bluetooth mesh networking technology 导言 蓝牙是当今最主要的低功耗无线技术之一,对无线设备用户和开 ...

  4. 最短路径问题,BFS,408方向,思路与实现分析

    最短路径问题,BFS,408方向,思路与实现分析 继上回挖下的坑,不知道大家有没有认真看最小生成树呢?很简单,这回也讲讲正常难度的,看不懂就来这里看看,讲的很好~~ 最短路径问题 说起这个问题,先说个 ...

  5. 692. 前K个高频单词

    2021-05-20 LeetCode每日一题 链接:https://leetcode-cn.com/problems/top-k-frequent-words/ 标签:堆.字典序.哈希表 题目 给一 ...

  6. jmeter+ant+jenkins框架搭建问题

    工具介绍:   jmeter 接口编写,调试,执行.   ant Ant是Java的生成工具,都是用来编译.生成:Ant的主要目的就是把你想做的事情自动化,不用你手动一步一步做,因为里面内置了java ...

  7. 我想挑战下我的软肋,动手实现个Spring应用上下文!

    作者:小傅哥 博客:https://bugstack.cn 沉淀.分享.成长,让自己和他人都能有所收获! 目录 [x] 第 1 章:开篇介绍,我要带你撸 Spring 啦! [x] 第 2 章:小试牛 ...

  8. csps前小结

    冒着题没改完颓废被发现的风险来写博客 好像离csps只剩两天了,然而没啥感觉 最近考试有时考得还算可以,有时也会很炸 今天考试事实上心态啥崩,因为T1结论题一直没思路,想了一个小时连暴力都没打 过了一 ...

  9. [源码解析] 深度学习分布式训练框架 horovod (6) --- 后台线程架构

    [源码解析] 深度学习分布式训练框架 horovod (6) --- 后台线程架构 目录 [源码解析] 深度学习分布式训练框架 horovod (6) --- 后台线程架构 0x00 摘要 0x01 ...

  10. 【NLP学习其二】什么是隐马尔可夫模型HMM?

    概念 隐马尔可夫模型描述的是两个时序序列联合分布p(x,y)的概率模型,其中包含了两个序列: x序列外界可见(外界指的是观测者),称为观测序列(obsevation seuence) y序列外界不可见 ...