为什么阿里巴巴要求谨慎使用ArrayList中的subList方法
GitHub 3.7k Star 的Java工程师成神之路 ,不来了解一下吗?
GitHub 3.7k Star 的Java工程师成神之路 ,真的不来了解一下吗?
GitHub 3.7k Star 的Java工程师成神之路 ,真的确定不来了解一下吗?
集合是Java开发日常开发中经常会使用到的。在之前的一些文章中,我们介绍过一些关于使用集合类应该注意的事项,如《为什么阿里巴巴禁止在 foreach 循环里进行元素的 remove/add 操作》、《为什么阿里巴巴建议集合初始化时,指定集合容量大小》等。
关于集合类,《阿里巴巴Java开发手册》中其实还有另外一个规定:

本文就来分析一下为什么会有如此建议?其背后的原理是什么?
subList
subList是List接口中定义的一个方法,该方法主要用于返回一个集合中的一段、可以理解为截取一个集合中的部分元素,他的返回值也是一个List。
如以下代码:
public static void main(String[] args) {
List<String> names = new ArrayList<String>() {{
add("Hollis");
add("hollischuang");
add("H");
}};
List subList = names.subList(0, 1);
System.out.println(subList);
}
以上代码输出结果为:
[Hollis]
如果我们改动下代码,将subList的返回值强转成ArrayList试一下:
public static void main(String[] args) {
List<String> names = new ArrayList<String>() {{
add("Hollis");
add("hollischuang");
add("H");
}};
ArrayList subList = names.subList(0, 1);
System.out.println(subList);
}
以上代码将抛出异常:
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的源码:
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,那么看下这个构造函数做了哪些事情:
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
public static void main(String[] args) {
List<String> sourceList = new ArrayList<String>() {{
add("H");
add("O");
add("L");
add("L");
add("I");
add("S");
}};
List subList = sourceList.subList(2, 5);
System.out.println("sourceList : " + sourceList);
System.out.println("sourceList.subList(2, 5) 得到List :");
System.out.println("subList : " + subList);
subList.set(1, "666");
System.out.println("subList.set(3,666) 得到List :");
System.out.println("subList : " + subList);
System.out.println("sourceList : " + sourceList);
}
得到结果:
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中对应的值也会发生改变。读者可以自行尝试一下。
1、结构性改变SubList
public static void main(String[] args) {
List<String> sourceList = new ArrayList<String>() {{
add("H");
add("O");
add("L");
add("L");
add("I");
add("S");
}};
List subList = sourceList.subList(2, 5);
System.out.println("sourceList : " + sourceList);
System.out.println("sourceList.subList(2, 5) 得到List :");
System.out.println("subList : " + subList);
subList.add("666");
System.out.println("subList.add(666) 得到List :");
System.out.println("subList : " + subList);
System.out.println("sourceList : " + sourceList);
}
得到结果:
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的结构也同样发生了改变。
1、结构性改变原List
public static void main(String[] args) {
List<String> sourceList = new ArrayList<String>() {{
add("H");
add("O");
add("L");
add("L");
add("I");
add("S");
}};
List subList = sourceList.subList(2, 5);
System.out.println("sourceList : " + sourceList);
System.out.println("sourceList.subList(2, 5) 得到List :");
System.out.println("subList : " + subList);
sourceList.add("666");
System.out.println("sourceList.add(666) 得到List :");
System.out.println("sourceList : " + sourceList);
System.out.println("subList : " + subList);
}
得到结果:
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开发手册中有另外一条规定:

如何创建新的List
如果需要对subList作出修改,又不想动原list。那么可以创建subList的一个拷贝:
subList = Lists.newArrayList(subList);
list.stream().skip(strart).limit(end).collect(Collectors.toList());
PS:最近,《阿里巴巴Java开发手册》已经正式更名为《Java开发手册》,并发布了新版本,增加了21条新规约,修改描述112处。
关注公众号后台回复:手册,即可获取最新版Java开发手册。
参考资料: https://www.jianshu.com/p/5854851240df https://www.cnblogs.com/ljdblog/p/6251387.html
为什么阿里巴巴要求谨慎使用ArrayList中的subList方法的更多相关文章
- 谨慎使用ArrayList中的subList方法
转自:https://www.toutiao.com/a6705958780460335619/?tt_from=weixin&utm_campaign=client_share&wx ...
- 49. ArrayList LinkedList中特有的方法
集合的体系:--------------| Collection 单列集合的根接口 ----------| List 如果实现了List接口的集合类,该类具备的特点是:有序,可重复 ------|A ...
- ArrayList中元素去重问题
如题所示,如果一个ArrayList中包含多个重复元素,该如何去重呢? 思路一以及实现: 声明2个ArrayList,分别为listA与listB ,listA为待去重list ,listB 保存去重 ...
- IOS开发中重写init方法使用需谨慎
IOS开发中重写init方法使用需谨慎 今天在写一个小软件的时候出现一点问题,这个软件的功能是搜索全国学校,首页就是搜索输入框,在框中输入完要查询的学校所在省份,点击buttom后就会跳转到对应的视图 ...
- Java 删除ArrayList中重复元素,保持顺序
// 删除ArrayList中重复元素,保持顺序 public static List<Map<String, Object>> removeDuplicat ...
- ArrayList类中的contains()方法底层依赖的是equals()方法
ArrayList类中的contains()方法底层依赖的是equals()方法.若集合中的元素是自定义对象,则应该重写该类父类Object的equals()方法,否则对象永远都不相同(因为都是new ...
- ArrayList list = new ArrayList()在这个泛型为Integer的ArrayList中存放一个String类型的对象
java面试要点---ArrayList list = new ArrayList(); 在这个泛型为Integer的ArrayList中存放一个String类型的对象. ArrayList list ...
- 如何使用 Java 删除 ArrayList 中的重复元素
如何使用 Java 删除 ArrayList 中的重复元素 (How to Remove Duplicates from ArrayList in Java) Given an ArrayList w ...
- Java删除ArrayList中的重复元素
Java删除ArrayList中的重复元素的2种方法 ArrayList是Java中最常用的集合类型之一.它允许灵活添加多个null元素,重复的元素,并保持元素的插入顺序.在编码时我们经常会遇到那种必 ...
随机推荐
- HDU 5649.DZY Loves Sorting-线段树+二分-当前第k个位置的数
DZY Loves Sorting Time Limit: 12000/6000 MS (Java/Others) Memory Limit: 262144/262144 K (Java/Oth ...
- hiho一下第130周 后缀自动机二·重复旋律7
后缀自动机四·重复旋律7 时间限制:15000ms 单点时限:3000ms 内存限制:512MB 描述 小Hi平时的一大兴趣爱好就是演奏钢琴.我们知道一段音乐旋律可以被表示为一段数构成的数列. 神奇的 ...
- hiho一下第128周 后缀自动机二·重复旋律5
#1445 : 后缀自动机二·重复旋律5 时间限制:10000ms 单点时限:2000ms 内存限制:512MB 描述 小Hi平时的一大兴趣爱好就是演奏钢琴.我们知道一个音乐旋律被表示为一段数构成的数 ...
- JQuery里面的下啦菜单
<!DOCTYPE html><html><head lang="en"> <meta charset="UTF-8" ...
- ASP.NET基础题(1-10)
1.简述 private. protected. public. internal 修饰符的访问权限. private : 私有成员, 在类的内部才可以访问. protected : 保护成员,该类内 ...
- iOS8下的远程推送
本篇文章主要介绍了"iOS8下的远程推送",主要涉及到方面的内容,对于IOS开发感兴趣的同学可以参考一下: 昨天做了一下远程推送,今天写下来,分享给需要的人.参考了很多篇文章,或许 ...
- u-boot-2015.01在tq2440上的初步移植
作者: 彭东林 邮箱: pengdonglin137@163.com QQ: 405728433 开发板: tq2440 工具: Win7 + VMware + Debian6 ...
- bmp,jpg,png,tif,wmf,emf与eps图片格式转换
wmf/emf是两种Microsoft Windows的图形文件格式.它是一个矢量图格式,但是也允许包含位图.本质上,一个WMF文件保存一系列可以用来重建图片的Windows GDI命令.在某种程度上 ...
- ylbtech-KeFuYunWei(服务运维考核系统)-数据库设计
ylbtech-DatabaseDesgin:ylbtech-KeFuYunWei(服务运维考核系统)-数据库设计 DatabaseName:KEFUYUNWEI Model:Admin 用户后台管理 ...
- 用android连小米手机做profiling
安装失败 Installation failed with message Failed to finalize session http://www.jianshu.com/p/fea7d96385 ...