集合是Java开发日常开发中经常会使用到的。在之前的一些文章中,我们介绍过一些关于使用集合类应该注意的事项,如《为什么阿里巴巴禁止在 foreach 循环里进行元素的 remove/add 操作》、《为什么阿里巴巴建议集合初始化时,指定集合容量大小》等。

关于集合类,《阿里巴巴Java开发手册》中其实还有另外一个规定:

本文就来分析一下为什么会有如此建议?其背后的原理是什么?

subList

subList是List接口中定义的一个方法,该方法主要用于返回一个集合中的一段、可以理解为截取一个集合中的部分元素,他的返回值也是一个List。

如以下代码:

以上代码输出结果为:


  1. [Hollis]

如果我们改动下代码,将subList的返回值强转成ArrayList试一下:

以上代码将抛出异常:


  1. java.lang.ClassCastException: java.util.ArrayList$SubList cannot be cast to java.util.ArrayList

不只是强转成ArrayList会报错,强转成LinkedList、Vector等List的实现类同样也都会报错。

那么,为什么会发生这样的报错呢?我们接下来深入分析一下。

底层原理

首先,我们看下subList方法给我们返回的List到底是个什么东西,这一点在JDK源码中注释是这样说的:

Returns a view of the portion of this list between the specifiedfromIndex, inclusive, and toIndex, exclusive.

也就是说subList 返回是一个视图,那么什么叫做视图呢?

我们看下subList的源码:


  1. public List<E> subList(int fromIndex, int toIndex) {
    subListRangeCheck(fromIndex, toIndex, size);
    return new SubList(this, 0, fromIndex, toIndex);
    }

这个方法返回了一个SubList,这个类是ArrayList中的一个内部类。

SubList这个类中单独定义了set、get、size、add、remove等方法。

当我们调用subList方法的时候,会通过调用SubList的构造函数创建一个SubList,那么看下这个构造函数做了哪些事情:


  1. SubList(AbstractList<E> parent,
    int offset, int fromIndex, int toIndex) {
    this.parent = parent;
    this.parentOffset = fromIndex;
    this.offset = offset + fromIndex;
    this.size = toIndex - fromIndex;
    this.modCount = ArrayList.this.modCount;
    }

可以看到,这个构造函数中把原来的List以及该List中的部分属性直接赋值给自己的一些属性了。

也就是说,SubList并没有重新创建一个List,而是直接引用了原有的List(返回了父类的视图),只是指定了一下他要使用的元素的范围而已(从fromIndex(包含),到toIndex(不包含))。

所以,为什么不能讲subList方法得到的集合直接转换成ArrayList呢?因为SubList只是ArrayList的内部类,他们之间并没有继承关系,故无法直接进行强制类型转换。

视图有什么问题

前面通过查看源码,我们知道,subList()方法并没有重新创建一个ArrayList,而是返回了一个ArrayList的内部类——SubList。

这个SubList是ArrayList的一个视图。

那么,这个视图又会带来什么问题呢?我们需要简单写几段代码看一下。

1、非结构性改变SubList

得到结果:


  1. sourceList : [H, O, L, L, I, S]
    sourceList.subList(2, 5) 得到List :
    subList : [L, L, I]
    subList.set(3,666) 得到List :
    subList : [L, 666, I]
    sourceList : [H, O, L, 666, I, S]

当我们尝试通过set方法,改变subList中某个元素的值得时候,我们发现,原来的那个List中对应元素的值也发生了改变。

同理,如果我们使用同样的方法,对sourceList中的某个元素进行修改,那么subList中对应的值也会发生改变。读者可以自行尝试一下。

2、结构性改变SubList

得到结果:


  1. sourceList : [H, O, L, L, I, S]
    sourceList.subList(2, 5) 得到List :
    subList : [L, L, I]
    subList.add(666) 得到List :
    subList : [L, L, I, 666]
    sourceList : [H, O, L, L, I, 666, S]

我们尝试对subList的结构进行改变,即向其追加元素,那么得到的结果是sourceList的结构也同样发生了改变。

3、结构性改变原List

得到结果:


  1. Exception in thread "main" java.util.ConcurrentModificationException
    at java.util.ArrayList$SubList.checkForComodification(ArrayList.java:1239)
    at java.util.ArrayList$SubList.listIterator(ArrayList.java:1099)
    at java.util.AbstractList.listIterator(AbstractList.java:299)
    at java.util.ArrayList$SubList.iterator(ArrayList.java:1095)
    at java.util.AbstractCollection.toString(AbstractCollection.java:454)
    at java.lang.String.valueOf(String.java:2994)
    at java.lang.StringBuilder.append(StringBuilder.java:131)
    at com.hollis.SubListTest.main(SubListTest.java:28)

我们尝试对sourceList的结构进行改变,即向其追加元素,结果发现抛出了ConcurrentModificationException。关于这个异常,我们在《一不小心就踩坑的fail-fast是个什么鬼?》中分析过,这里原理相同,就不再赘述了。

小结

我们简单总结一下,List的subList方法并没有创建一个新的List,而是使用了原List的视图,这个视图使用内部类SubList表示。

所以,我们不能把subList方法返回的List强制转换成ArrayList等类,因为他们之间没有继承关系。

另外,视图和原List的修改还需要注意几点,尤其是他们之间的相互影响:

  • 1、对父(sourceList)子(subList)List做的非结构性修改(non-structural changes),都会影响到彼此。
  • 2、对子List做结构性修改,操作同样会反映到父List上。
  • 3、对父List做结构性修改,会抛出异常ConcurrentModificationException。

所以,阿里巴巴Java开发手册中有另外一条规定:

5

如何创建新的List

如果需要对subList作出修改,又不想动原list。那么可以创建subList的一个拷贝:


  1. subList = Lists.newArrayList(subList);
    list.stream().skip(strart).limit(end).collect(Collectors.toList());

使用ArrayList中的subList方法相关推荐

  1. java ArrayList中的subList方法

    2019独角兽企业重金招聘Python工程师标准>>> 本文是本人的学习笔记,把自己的理解总结记录下来.因本人水平有限,如果您在阅读中发现错误,还望谅解,并且希望能够告知本人改正,不 ...

  2. 原创 | 为什么阿里巴巴要求谨慎使用ArrayList中的subList方法

    △Hollis, 一个对Coding有着独特追求的人△ 这是Hollis的第 219 篇原创分享 作者 l Hollis 来源 l Hollis(ID:hollischuang) 集合是Java开发日 ...

  3. java list sublist方法_聊聊ArrayList中的subList方法

    开发过程中遇到的坑 开发过程经常会使用subList做分页处理. 比如下面的代码 while(pageIndex < maxSize) { List temp = userIds.subList ...

  4. Java中的subList方法

    Java中的subList方法 今天看到了java中List中有个subList的方法,感觉很熟悉有没有?没错,在Stirng类中,也有个类似的方法:subString. Stirng中的subStr ...

  5. java中list中sublist_Java 中 List.subList() 方法的使用陷阱

    前言 本文原先发表在我的 iteye博客: http://clevergump.iteye.com/admin/blogs/2211979, 但由于在 iteye发表的这篇文章的某些渲染曾经出现过一些 ...

  6. java list sublist方法_(转)Java 中 List.subList() 方法的使用陷阱

    原文:http://blog.csdn.net/cleverGump/article/details/51105235 前言 本文原先发表在我的 iteye博客: http://clevergump. ...

  7. java中list中的subList方法

    List<Object> list = new Arraylist<>(); List<Object> subList = list.subList(0, 5); ...

  8. java sublist_java中List.subList()方法的使用

    sublist返回的东西,官方解释:Returns a view of the portion of this list between the specified fromIndex, inclus ...

  9. List中subList方法抛出异常java.util.ConcurrentModificationException原理分析

    1.首先从测试代码开始: public class Test {public static void main(String[] args) {List<Integer> list = n ...

最新文章

  1. #418 Div2 Problem B An express train to reveries (构造 || 全排列序列特性)
  2. 初级Java程序员所面临的4大挑战
  3. 035_使用Enumeration遍历Vector元素
  4. 【NGN学习笔记】5 IMS技术
  5. 虚拟化精华问答 | 什么是虚拟化?
  6. java停启was集群_shell脚本实现weblogic 节点启停,应用部署
  7. isset php 二维数组_php 数组去重,一维数组去重,二维数组去重
  8. js实现数据流(日志流,报警信息等)滚动展示,并分页(含实现demo)
  9. 闪退没由报错_使命召唤:(cod16)出现的闪退问题以及解决办法
  10. Java String到int,Java int到String
  11. 减小iOS应用程序的大小
  12. FluorineFx:远程共享对象(Remote SharedObjects)
  13. RMAN 总括 组成 配置 检测
  14. event mpm php,apache的mpm的几种模式
  15. python输入三个数形成各种三角形
  16. 微博运营的5个经典案例
  17. 基于java写的雷霆战机
  18. 一键反编译安卓apk文件
  19. 数据压缩作业2.1 多媒体文件分析——TGA文件
  20. Spring boot在线客服系统源码 在线坐席对话源码

热门文章

  1. h5同层播放器的知识
  2. 靠谱的区块链应用到底是啥?通证啊!——第二次中关村-CSDN区块链开发技术沙龙【含PPT下载】
  3. SCAU19023 砍树
  4. 产业分析:2022Q3全球独角兽
  5. 查看iPhone电池寿命
  6. 学习笔记-无线的简单配置
  7. 产品读书《互联网思维独孤九剑:移动互联网时代的思维革命》
  8. JavaScipt基础(持续更新二)
  9. 感染[熊猫烧香变种 spoclsv.exe]
  10. 信捷伺服驱动器调试记录