subList方法拆分集合问题
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个元素的集合拆分为两个集合aLis和bList,然后创建一个新的集合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方法拆分集合问题的更多相关文章
- List集合数据太多进行分批,List的subList方法应用
		
List<String> mStrings=new ArrayList<>(); //初始化 for (int i = 0; i < 1020; i++) { mStri ...
 - java List.subList方法中的超级大陷阱
		
ArrayList 中 subList 的基本用法: subList(fromIndex:int,toIndex:int):List<E> 返回从fromIndex到toindex-1 的 ...
 - 使用java.util.List的subList方法进行分页
		
java.util.List中有一个subList方法,用来返回一个list的一部分视图. List<E> subList(int fromIndex, int toIndex); 它返回 ...
 - 为什么阿里巴巴要求谨慎使用ArrayList中的subList方法
		
GitHub 3.7k Star 的Java工程师成神之路 ,不来了解一下吗? GitHub 3.7k Star 的Java工程师成神之路 ,真的不来了解一下吗? GitHub 3.7k Star 的 ...
 - 谨慎使用ArrayList中的subList方法
		
转自:https://www.toutiao.com/a6705958780460335619/?tt_from=weixin&utm_campaign=client_share&wx ...
 - ArrayList.subList方法使用总结
		
ArrayList.subList方法使用总结 示例 List<String> list=new ArrayList<>(); list.add("d"); ...
 - JVM-class文件完全解析-方法表集合
		
方法表集合 前面的魔数,次版本号,主板本号,常量池入口,常量池,访问标志,类索引,父类索引,接口索引集合,字段表集合,那么再接下来就是方法表了. 方法表的构造如同字段表一样,依次包括了访问标志(a ...
 - 需要注意的subList方法!和substring是不一样的!从源码解释他们的不同。
		
很多时候我们截取字符串用的是substring方法,很自然用着,但是对于列表的截取时很多时候就用得很少,但是其实他们是很不一样的,具体哪里不一样呢? package main; import java ...
 - js/jq基础(日常整理记录)-2-一个简单的js方法实现集合的非引用拷贝
		
一.一个简单的js方法实现集合拷贝 做web项目的时候,少不了和js中的数组,集合等对象接触,那么你肯定会发现,在js中存在一个怪异的现象就是数组和集合的拷贝都是地址复制,并不是简单的数据的拷贝. 举 ...
 
随机推荐
- Jmeter——元件扩展,使其功能更全面
			
工具扩展 在之前的博文中,有介绍自定义函数.Java请求扩展,博文如下: Jmeter二次开发--基于Java请求 Jmeter二次开发--自定义函数 上述内容,是按自己的需要来进行针对性扩展,从而实 ...
 - CUDA 11功能展示
			
CUDA 11功能展示 CUDA 11 Features Revealed 新的NVIDIA A100 GPU基于NVIDIA安培GPU架构,实现了加速计算的最大一代飞跃.A100 GPU具有革命性的 ...
 - 构造无限级树的框架套路,附上python/golang/php/js实现
			
目录 前言 需求 数据 结果 框架 递归框架 迭代框架 递归框架实现 python golang php js 迭代框架实现 python golang php js 前言 框架思维非常重要,和语言无 ...
 - 【Python报错】RuntimeError: DataLoader worker (pid(s) 9764, 15128) exited unexpectedly
			
batch_size = 2# 256 def get_dataloader_workers(): #@save """使用4个进程来读取数据.""& ...
 - Ckeditor 缺少图像源文件地址的解决 笨笨的人都看啦!
			
Ckeditor 本文是关于CKEditor 无法上传图片问题的一个解决.我大致写了一下遇到问题的过程,问题的出处,怎么解决的,原因是什么. 希望能够帮到有需要的大家,有些时候找不到问题的答案,真的是 ...
 - 【NX二次开发】Block UI   选择表达式
			
属性说明 属性 类型 描述 常规 BlockID String 控件ID Enable Logical 是否可操作 Group ...
 - 屌炸天,像写代码一样写PPT,一个小工具解决
			
此文已经废,请移步升级版博文: markdown写ppt (史上最全)
 - [非专业翻译] 高性能对象映射框架 - Mapster
			
[非专业翻译] 高性能对象映射框架 - Mapster 系列介绍 [非专业翻译] 是对没有中文文档进行翻译的系列博客,文章由机翻和译者自己理解构成,和原文相比有所有不通,但意思基本一致. 因个人能力有 ...
 - SprignBoot是如何访问工程目录下的静态资源?
			
 目录 1.牛刀小试 1.1 图片静态资源的访问 1.2 为静态资源添加访问前缀 1.3 WelCome Page 的奇妙跳转 2.那么,SpringBoot是如何做到的呢?  1. ...
 - VBS脚本编程(5)——过程与函数
			
过程是构成程序的一个模块,往往用来完成一个相对独立的功能.过程可以使程序更清. Sub过程与Function函数的区别: Sub没有返回值,Function有返回值: Sub不能放在表达式中,Func ...