亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

為什么阿里要慎重使用ArrayList中的subList方法

 更新時間:2019年06月25日 14:51:28   作者:HollisChuang  
這篇文章主要介紹了為什么要慎重使用ArrayList中的subList方法,subList是List接口中定義的一個方法,該方法主要用于返回一個集合中的一段、可以理解為截取一個集合中的部分元素,他的返回值也是一個List。,需要的朋友可以參考下

前言

集合是Java開發(fā)日常開發(fā)中經(jīng)常會使用到的。

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

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

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);
}

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

[Hollis]

如果我們改動下代碼,將subList的返回值強(qiáng)轉(zhuǎn)成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

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

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

底層原理

首先,我們看下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中的一個內(nèi)部類。

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

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

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;
}

可以看到,這個構(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)換。

視圖有什么問題

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

這個SubList是ArrayList的一個視圖。

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

非結(jié)構(gòu)性改變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);
}

得到結(jié)果:

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]

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

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

結(jié)構(gòu)性改變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);
}

得到結(jié)果:

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的結(jié)構(gòu)進(jìn)行改變,即向其追加元素,那么得到的結(jié)果是sourceList的結(jié)構(gòu)也同樣發(fā)生了改變。

結(jié)構(gòu)性改變原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);
}

得到結(jié)果:

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的結(jié)構(gòu)進(jìn)行改變,即向其追加元素,結(jié)果發(fā)現(xiàn)拋出了ConcurrentModificationException。

小結(jié)

我們簡單總結(jié)一下,List的subList方法并沒有創(chuàng)建一個新的List,而是使用了原List的視圖,這個視圖使用內(nèi)部類SubList表示。

所以,我們不能把subList方法返回的List強(qiáng)制轉(zhuǎn)換成ArrayList等類,因為他們之間沒有繼承關(guān)系。

另外,視圖和原List的修改還需要注意幾點,尤其是他們之間的相互影響:

1、對父(sourceList)子(subList)List做的非結(jié)構(gòu)性修改(non-structural changes),都會影響到彼此。

2、對子List做結(jié)構(gòu)性修改,操作同樣會反映到父List上。

3、對父List做結(jié)構(gòu)性修改,會拋出異常ConcurrentModificationException。

所以,阿里巴巴Java開發(fā)手冊中有另外一條規(guī)定:

如何創(chuàng)建新的List

如果需要對subList作出修改,又不想動原list。那么可以創(chuàng)建subList的一個拷貝:

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

以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • SparkSQL開窗函數(shù)分析使用示例

    SparkSQL開窗函數(shù)分析使用示例

    開窗函數(shù)的引入是為了既顯示聚集前的數(shù)據(jù),又顯示聚集后的數(shù)據(jù)。即在每一行的最后一列添加聚合函數(shù)的結(jié)果。開窗用于為行定義一個窗口,它對一組值進(jìn)行操作,不需要使用 GROUP BY 子句對數(shù)據(jù)進(jìn)行分組,能夠在同一行中同時返回基礎(chǔ)行的列和聚合列
    2023-01-01
  • Java 自旋鎖(spinlock)相關(guān)知識總結(jié)

    Java 自旋鎖(spinlock)相關(guān)知識總結(jié)

    這篇文章主要介紹了Java 自旋鎖(spinlock)相關(guān)知識總結(jié),幫助大家更好的理解和使用Java,感興趣的朋友可以了解下
    2021-02-02
  • idea2017建立jsp工程及tomcat配置教程

    idea2017建立jsp工程及tomcat配置教程

    本文通過圖文并茂的形式給大家介紹了idea2017建立jsp工程及tomcat等配置的方法,非常不錯,具有參考借鑒價值,需要的朋友可以參考下
    2018-02-02
  • Spring?Boot?整合?FreeMarker?實例分享

    Spring?Boot?整合?FreeMarker?實例分享

    這篇文章主要分享了Spring?Boot整合FreeMarker?實例FreeMarker是一款模板引擎,即一種基于模板和要改變的數(shù)據(jù),并用來生成輸出文本,更多相關(guān)介紹需要的小伙伴可以參考下面文章內(nèi)容
    2022-05-05
  • Java基礎(chǔ)之打印萬年歷的簡單實現(xiàn)(案例)

    Java基礎(chǔ)之打印萬年歷的簡單實現(xiàn)(案例)

    下面小編就為大家?guī)硪黄狫ava基礎(chǔ)之打印萬年歷的簡單實現(xiàn)(案例)。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2016-07-07
  • 基于TCP通信丟包原因總結(jié)(推薦)

    基于TCP通信丟包原因總結(jié)(推薦)

    下面小編就為大家?guī)硪黄赥CP通信丟包原因總結(jié)(推薦)。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-08-08
  • JPA save()方法將字段更新為null的解決方案

    JPA save()方法將字段更新為null的解決方案

    這篇文章主要介紹了JPA save()方法將字段更新為null的解決方案,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2020-01-01
  • Spring MVC簡介_動力節(jié)點Java學(xué)院整理

    Spring MVC簡介_動力節(jié)點Java學(xué)院整理

    Spring MVC屬于SpringFrameWork的后續(xù)產(chǎn)品,已經(jīng)融合在Spring Web Flow里面。今天先從寫一個Spring MVC的HelloWorld開始,讓我們看看如何搭建起一個Spring mvc的環(huán)境并運行程序,感興趣的朋友一起學(xué)習(xí)吧
    2017-08-08
  • VMware虛擬機(jī)下hadoop1.x的安裝方法

    VMware虛擬機(jī)下hadoop1.x的安裝方法

    這篇文章主要為大家詳細(xì)介紹了VMware虛擬機(jī)下hadoop1.x的安裝方法,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-09-09
  • Spring中@Autowired自動注入map詳解

    Spring中@Autowired自動注入map詳解

    這篇文章主要介紹了Spring中@Autowired自動注入map詳解,  spring是支持基于接口實現(xiàn)類的直接注入的,支持注入map,list等集合中,不用做其他的配置,直接注入,需要的朋友可以參考下
    2023-10-10

最新評論