自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

為什么阿里巴巴要求謹(jǐn)慎使用ArrayList中的subList方法

開發(fā) 開發(fā)工具
subList是List接口中定義的一個方法,該方法主要用于返回一個集合中的一段、可以理解為截取一個集合中的部分元素,他的返回值也是一個List。

[[268976]]

集合是Java開發(fā)日常開發(fā)中經(jīng)常會使用到的。在之前的一些文章中,我們介紹過一些關(guān)于使用集合類應(yīng)該注意的事項,如《為什么阿里巴巴禁止在 foreach 循環(huán)里進(jìn)行元素的 remove/add 操作》、《為什么阿里巴巴建議集合初始化時,指定集合容量大小》等。

關(guān)于集合類,《阿里巴巴Java開發(fā)手冊》中其實還有另外一個規(guī)定:

 

本文就來分析一下為什么會有如此建議?其背后的原理是什么?

1.subList

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

如以下代碼:

  1. public static void main(String[] args) { 
  2.     List<String> names = new ArrayList<String>() {{ 
  3.         add("Hollis"); 
  4.         add("hollischuang"); 
  5.         add("H"); 
  6.     }}; 
  7.  
  8.     List subList = names.subList(0, 1); 
  9.     System.out.println(subList); 

以上代碼輸出結(jié)果為:

  1. [Hollis] 

如果我們改動下代碼,將subList的返回值強(qiáng)轉(zhuǎn)成ArrayList試一下:

  1. public static void main(String[] args) { 
  2.     List<String> names = new ArrayList<String>() {{ 
  3.         add("Hollis"); 
  4.         add("hollischuang"); 
  5.         add("H"); 
  6.     }}; 
  7.  
  8.     ArrayList subList = names.subList(0, 1); 
  9.     System.out.println(subList); 

以上代碼將拋出異常:

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

不只是強(qiáng)轉(zhuǎn)成ArrayList會報錯,強(qiáng)轉(zhuǎn)成LinkedList、Vector等List的實現(xiàn)類同樣也都會報錯。

那么,為什么會發(fā)生這樣的報錯呢?我們接下來深入分析一下。

2.底層原理

首先,我們看下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) { 
  2.     subListRangeCheck(fromIndex, toIndex, size); 
  3.     return new SubList(this, 0, fromIndex, toIndex); 

這個方法返回了一個SubList,這個類是ArrayList中的一個內(nèi)部類。

SubList這個類中單獨(dú)定義了set、get、size、add、remove等方法。

當(dāng)我們調(diào)用subList方法的時候,會通過調(diào)用SubList的構(gòu)造函數(shù)創(chuàng)建一個SubList,那么看下這個構(gòu)造函數(shù)做了哪些事情:

  1. SubList(AbstractList<E> parent, 
  2.             int offset, int fromIndex, int toIndex) { 
  3.     this.parent = parent; 
  4.     this.parentOffset = fromIndex; 
  5.     this.offset = offset + fromIndex; 
  6.     this.size = toIndex - fromIndex; 
  7.     this.modCount = ArrayList.this.modCount; 

可以看到,這個構(gòu)造函數(shù)中把原來的List以及該List中的部分屬性直接賦值給自己的一些屬性了。

也就是說,SubList并沒有重新創(chuàng)建一個List,而是直接引用了原有的List(返回了父類的視圖),只是指定了一下他要使用的元素的范圍而已(從fromIndex(包含),到toIndex(不包含))。

所以,為什么不能講subList方法得到的集合直接轉(zhuǎn)換成ArrayList呢?因為SubList只是ArrayList的內(nèi)部類,他們之間并沒有繼承關(guān)系,故無法直接進(jìn)行強(qiáng)制類型轉(zhuǎn)換。

3.視圖有什么問題

前面通過查看源碼,我們知道,subList()方法并沒有重新創(chuàng)建一個ArrayList,而是返回了一個ArrayList的內(nèi)部類——SubList。

這個SubList是ArrayList的一個視圖。

那么,這個視圖又會帶來什么問題呢?我們需要簡單寫幾段代碼看一下。

1、非結(jié)構(gòu)性改變SubList

  1. public static void main(String[] args) { 
  2.     List<String> sourceList = new ArrayList<String>() {{ 
  3.         add("H"); 
  4.         add("O"); 
  5.         add("L"); 
  6.         add("L"); 
  7.         add("I"); 
  8.         add("S"); 
  9.     }}; 
  10.  
  11.     List subList = sourceList.subList(2, 5); 
  12.  
  13.     System.out.println("sourceList : " + sourceList); 
  14.     System.out.println("sourceList.subList(2, 5) 得到List :"); 
  15.     System.out.println("subList : " + subList); 
  16.  
  17.     subList.set(1, "666"); 
  18.  
  19.     System.out.println("subList.set(3,666) 得到List :"); 
  20.     System.out.println("subList : " + subList); 
  21.     System.out.println("sourceList : " + sourceList); 

得到結(jié)果:

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

當(dāng)我們嘗試通過set方法,改變subList中某個元素的值得時候,我們發(fā)現(xiàn),原來的那個List中對應(yīng)元素的值也發(fā)生了改變。

同理,如果我們使用同樣的方法,對sourceList中的某個元素進(jìn)行修改,那么subList中對應(yīng)的值也會發(fā)生改變。讀者可以自行嘗試一下。

2、結(jié)構(gòu)性改變SubList

  1. public static void main(String[] args) { 
  2.     List<String> sourceList = new ArrayList<String>() {{ 
  3.         add("H"); 
  4.         add("O"); 
  5.         add("L"); 
  6.         add("L"); 
  7.         add("I"); 
  8.         add("S"); 
  9.     }}; 
  10.  
  11.     List subList = sourceList.subList(2, 5); 
  12.  
  13.     System.out.println("sourceList : " + sourceList); 
  14.     System.out.println("sourceList.subList(2, 5) 得到List :"); 
  15.     System.out.println("subList : " + subList); 
  16.  
  17.     subList.add("666"); 
  18.  
  19.     System.out.println("subList.add(666) 得到List :"); 
  20.     System.out.println("subList : " + subList); 
  21.     System.out.println("sourceList : " + sourceList); 
  22.  

得到結(jié)果:

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

我們嘗試對subList的結(jié)構(gòu)進(jìn)行改變,即向其追加元素,那么得到的結(jié)果是sourceList的結(jié)構(gòu)也同樣發(fā)生了改變。

3、結(jié)構(gòu)性改變原List

  1. public static void main(String[] args) { 
  2.     List<String> sourceList = new ArrayList<String>() {{ 
  3.         add("H"); 
  4.         add("O"); 
  5.         add("L"); 
  6.         add("L"); 
  7.         add("I"); 
  8.         add("S"); 
  9.     }}; 
  10.  
  11.     List subList = sourceList.subList(2, 5); 
  12.  
  13.     System.out.println("sourceList : " + sourceList); 
  14.     System.out.println("sourceList.subList(2, 5) 得到List :"); 
  15.     System.out.println("subList : " + subList); 
  16.  
  17.     sourceList.add("666"); 
  18.  
  19.     System.out.println("sourceList.add(666) 得到List :"); 
  20.     System.out.println("sourceList : " + sourceList); 
  21.     System.out.println("subList : " + subList); 
  22.  

得到結(jié)果:

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

我們嘗試對sourceList的結(jié)構(gòu)進(jìn)行改變,即向其追加元素,結(jié)果發(fā)現(xiàn)拋出了ConcurrentModificationException。

【本文是51CTO專欄作者Hollis的原創(chuàng)文章,作者微信公眾號Hollis(ID:hollischuang)】

 

戳這里,看該作者更多好文

責(zé)任編輯:武曉燕 來源: 51CTO
相關(guān)推薦

2019-09-02 15:20:28

Java開發(fā)繼承

2019-09-04 11:02:54

繼承層次組合

2018-12-29 15:41:41

阿里巴巴程序員serialVersi

2021-10-11 09:32:40

包裝類型屬性

2022-03-14 09:41:10

POJO類型系統(tǒng)

2018-10-16 15:34:17

阿里巴巴Apache Flin大數(shù)據(jù)

2020-09-22 11:40:53

BigDecimalequalsJava

2021-08-04 17:20:30

阿里巴巴AsyncJava

2020-09-14 09:47:56

Java開發(fā)類型

2013-08-22 09:26:38

去IOE王堅

2020-07-30 12:16:33

阿里巴巴Apache對象

2020-09-08 16:25:18

Apache BeancopyJava

2023-04-03 07:03:51

阿里巴巴List元素

2016-09-21 20:28:55

阿里巴巴IOE

2022-09-05 10:06:21

MySQL外循環(huán)內(nèi)循環(huán)

2021-09-07 17:22:43

阿里巴巴辭職高薪

2025-04-17 08:47:23

2010-06-28 10:43:47

2022-08-22 08:07:45

DruidMySQL密碼

2021-10-20 14:53:31

Foreach強(qiáng)制阿里巴巴
點贊
收藏

51CTO技術(shù)棧公眾號