日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

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

發布時間:2025/3/16 编程问答 20 豆豆
生活随笔 收集整理的這篇文章主要介紹了 原创 | 为什么阿里巴巴要求谨慎使用ArrayList中的subList方法 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

△Hollis, 一個對Coding有著獨特追求的人△

這是Hollis的第?219?篇原創分享

作者 l Hollis

來源 l Hollis(ID:hollischuang)

?

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

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

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

1

subList

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

如以下代碼:

以上代碼輸出結果為:

[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的實現類同樣也都會報錯。

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

2

底層原理

首先,我們看下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的內部類,他們之間并沒有繼承關系,故無法直接進行強制類型轉換。

3

視圖有什么問題

前面通過查看源碼,我們知道,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中對應的值也會發生改變。讀者可以自行嘗試一下。

2、結構性改變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的結構也同樣發生了改變。

3、結構性改變原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是個什么鬼?》中分析過,這里原理相同,就不再贅述了。

4

小結

我們簡單總結一下,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的一個拷貝:

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

?

如果你喜歡本文,

請長按二維碼,關注?Hollis.

?

總結

以上是生活随笔為你收集整理的原创 | 为什么阿里巴巴要求谨慎使用ArrayList中的subList方法的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。