1. 程式人生 > 實用技巧 >給我半首歌的時間,給你說明白Immutable List

給我半首歌的時間,給你說明白Immutable List

先看再點贊,給自己一點思考的時間,微信搜尋【沉默王二】關注這個靠才華苟且的程式設計師。
本文 GitHub github.com/itwanger 已收錄,裡面還有一線大廠整理的面試題,以及我的系列文章。

Immutable List,顧名思義,就是,啥,不明白 Immutable 是什麼意思?一成不變的意思,所以 Immutable List 就是一個不可變的 List 類,這意味著該 List 聲明後,它的內容就是固定的,不可增刪改的。

如果對不可變類比較陌生的話,可以先點選下面的連結檢視我之前寫的另外一篇文章。

這次要說不明白immutable類,我就怎麼地

如果嘗試對 List 中的元素進行增加、刪除或者更新,就會丟擲 UnsupportedOperationException 異常。

另外,Immutable List 中的元素是非 null 的,如果使用 null 來建立 Immutable List,則會丟擲 NullPointerException;如果嘗試在 Immutable List 中新增 null 元素,則會丟擲 UnsupportedOperationException。

那 Immutable List 有什麼好處呢?

  • 它是執行緒安全的;
  • 它是高效的;
  • 因為它是不可變的,就可以像 String 一樣傳遞給第三方類庫,不會發生任何安全問題。

那接下來,我們來看一下,如何建立 Immutable List。注意,原始碼是基於 JDK14 的。

01、藉助原生 JDK

Collections 類的 unmodifiableList() 方法可以建立一個類似於 Immutable List 的 UnmodifiableList 或者 UnmodifiableRandomAccessList,都是不可修改的。

publicstatic<T>List<T>unmodifiableList(List<?extendsT>list){
return(listinstanceofRandomAccess?
newCollections.UnmodifiableRandomAccessList<>(list):
newCollections.UnmodifiableList<>(list));
}

來看一下使用方法:

List<String>list=newArrayList<>(Arrays.asList("沉默王二","沉默王三","沉默王四"));
List<String>unmodifiableList=Collections.unmodifiableList(list);

我們嘗試往 unmodifiableList 中新增元素“沉默王五”:

unmodifiableList.add("沉默王五");

執行後會丟擲 UnsupportedOperationException 異常:

Exceptioninthread"main"java.lang.UnsupportedOperationException
atjava.base/java.util.Collections$UnmodifiableCollection.add(Collections.java:1062)
atcom.cmower.mkyong.immutablelist.ImmutableListDemo.main(ImmutableListDemo.java:16)

02、藉助 Java 9

Java 9 的時候,List 類新增了一個 of() 靜態工廠方法,可以用來建立不可變的 List。先來看一下原始碼:

static<E>List<E>of(Ee1,Ee2,Ee3){
returnnewImmutableCollections.ListN<>(e1,e2,e3);
}

of() 方法有很多變體,比如說:

static<E>List<E>of(Ee1){
returnnewImmutableCollections.List12<>(e1);
}
static<E>List<E>of(Ee1,Ee2){
returnnewImmutableCollections.List12<>(e1,e2);
}
static<E>List<E>of(Ee1,Ee2,Ee3,Ee4){
returnnewImmutableCollections.ListN<>(e1,e2,e3,e4);
}
static<E>List<E>of(Ee1,Ee2,Ee3,Ee4,Ee5){
returnnewImmutableCollections.ListN<>(e1,e2,e3,e4,e5);
}
static<E>List<E>of(Ee1,Ee2,Ee3,Ee4,Ee5,Ee6,Ee7,Ee8,Ee9,Ee10){
returnnewImmutableCollections.ListN<>(e1,e2,e3,e4,e5,
e6,e7,e8,e9,e10);
}

該方法的設計者也挺有意思的,of() 方法的引數,從 0 到 10 都有一個相同簽名的過載方法。

甚至當引數是可變的時候,使用 switch 語句對引數的個數進行了判斷,然後呼叫不同的過載方法:

static<E>List<E>of(E...elements){
switch(elements.length){//implicitnullcheckofelements
case0:
@SuppressWarnings("unchecked")
varlist=(List<E>)ImmutableCollections.ListN.EMPTY_LIST;
returnlist;
case1:
returnnewImmutableCollections.List12<>(elements[0]);
case2:
returnnewImmutableCollections.List12<>(elements[0],elements[1]);
default:
returnnewImmutableCollections.ListN<>(elements);
}
}

不管是 ImmutableCollections.List12 還是 ImmutableCollections.ListN,它們都是 final 的,並且繼承了 AbstractImmutableList,裡面的元素也是 final 的。

staticfinalclassList12<E>extendsImmutableCollections.AbstractImmutableList<E>
implementsSerializable
{

@Stable
privatefinalEe0;

@Stable
privatefinalEe1;
}

staticfinalclassListN<E>extendsImmutableCollections.AbstractImmutableList<E>
implementsSerializable
{

//EMPTY_LISTmaybeinitializedfromtheCDSarchive.
static@StableList<?>EMPTY_LIST;

static{
VM.initializeFromArchive(ImmutableCollections.ListN.class);
if(EMPTY_LIST==null){
EMPTY_LIST=newImmutableCollections.ListN<>();
}
}

@Stable
privatefinalE[]elements;
}

好了,來看一下使用方法吧:

finalList<String>unmodifiableList=List.of("沉默王二","沉默王三","沉默王四");
unmodifiableList.add("沉默王五");

ImmutableCollections 的內部類 ListN 或者 List12 同樣不可修改,使用 add() 方法新增元素同樣會在執行時丟擲異常:

Exceptioninthread"main"java.lang.UnsupportedOperationException
atjava.base/java.util.ImmutableCollections.uoe(ImmutableCollections.java:73)
atjava.base/java.util.ImmutableCollections$AbstractImmutableCollection.add(ImmutableCollections.java:77)
atcom.cmower.mkyong.immutablelist.ImmutableListDemo.main(ImmutableListDemo.java:20)

03、藉助 Guava

Guava 工程包含了若干被 Google 的 Java 專案廣泛依賴的核心庫,例如:集合 [collections] 、快取 [caching] 、原生型別支援 [primitives support] 、併發庫 [concurrency libraries] 、通用註解 [common annotations] 、字串處理 [string processing] 、I/O 等等。 所有這些工具每天都在被 Google 的工程師應用在產品服務中。

在實際的專案實戰當中,Guava 類庫的使用頻率真的蠻高的,因此我們需要在專案中先引入 Guava 的 Maven 依賴。

<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>28.1-jre</version>
</dependency>

Guava 定了一個 ImmutableList 類,它的宣告方式如下所示:

@GwtCompatible(serializable=true,emulated=true)
@SuppressWarnings("serial")//we'reoverridingdefaultserialization
publicabstractclassImmutableList<E>extendsImmutableCollection<E>
implementsList<E>,RandomAccess
{
}

它的類結構關係如下所示:

java.lang.Object
↳java.util.AbstractCollection
↳com.google.common.collect.ImmutableCollection
↳com.google.common.collect.ImmutableList

ImmutableList 類的 copyOf() 方法可用於建立一個不可變的 List 物件:

List<String>list=newArrayList<>(Arrays.asList("沉默王二","沉默王三","沉默王四"));
List<String>unmodifiableList=ImmutableList.copyOf(list);
unmodifiableList.add("沉默王五");

ImmutableList 同樣不允許新增元素,add() 方法在執行的時候會丟擲 UnsupportedOperationException 異常:

Exceptioninthread"main"java.lang.UnsupportedOperationException
atcom.google.common.collect.ImmutableCollection.add(ImmutableCollection.java:244)
atcom.cmower.mkyong.immutablelist.ImmutableListDemo.main(ImmutableListDemo.java:25)

ImmutableList 類的 of() 方法和 Java 9 的 of() 方法類似,同樣有很多相同簽名的過載方法,使用方法也完全類似:

List<String>unmodifiableList=ImmutableList.of("沉默王二","沉默王三","沉默王四");

ImmutableList 類還提供了 builder 模式,既可以在建立的時候新增元素,也可以基於已有的 List 建立,還可以將兩者混合在一起。

ImmutableList<String>iList=ImmutableList.<String>builder()
.add("沉默王二","沉默王三","沉默王四")
.build();

List<String>list=List.of("沉默王二","沉默王三","沉默王四");
ImmutableList<String>iList=ImmutableList.<String>builder()
.addAll(list)
.build();

List<String>list=List.of("沉默王二","沉默王三","沉默王四");
ImmutableList<String>iList=ImmutableList.<String>builder()
.addAll(list)
.add("沉默王五")
.build();

04、Collections.unmodifiableList() 和 ImmutableList 有什麼區別?

Collections.unmodifiableList() 基於原有的 List 建立了一個不可變的包裝器,該包裝器是不可修改的,但是,我們可以通過對原有的 List 進行修改,從而影響到包裝器,來看下面的示例:

List<String>list=newArrayList<>();
list.add("沉默王二");

List<String>iList=Collections.unmodifiableList(list);

list.add("沉默王三");
list.add("沉默王四");

System.out.println(iList);

程式輸出的結果如下所示:

[沉默王二,沉默王三,沉默王四]

但如果我們通過 ImmutableList 類建立一個不可變 List,原有 List 的改變並不會影響到 ImmutableList。

List<String>list=newArrayList<>();
list.add("沉默王二");

ImmutableList<String>iList=ImmutableList.copyOf(list);

list.add("沉默王三");
list.add("沉默王四");

System.out.println(iList);

程式輸出的結果如下所示:

[沉默王二]

這是因為 ImmutableList 是在原有的 List 上進行了拷貝。


我是沉默王二,一枚有顏值卻靠才華苟且的程式設計師。關注即可提升學習效率,別忘了三連啊,點贊、收藏、留言,我不挑,奧利給

注:如果文章有任何問題,歡迎毫不留情地指正。

如果你覺得文章對你有些幫助歡迎微信搜尋「沉默王二」第一時間閱讀,回覆「小白」更有我肝了 4 萬+字的 Java 小白手冊 2.0 版,本文 GitHub github.com/itwanger 已收錄,歡迎 star。