給我半首歌的時間,給你說明白Immutable List
- 2020 年 7 月 27 日
- 筆記
- Immutable List, JAVA, 有趣Java
先看再點贊,給自己一點思考的時間,微信搜索【沉默王二】關注這個靠才華苟且的程式設計師。
本文 GitHub github.com/itwanger 已收錄,裡面還有一線大廠整理的面試題,以及我的系列文章。
Immutable List,顧名思義,就是,啥,不明白 Immutable 是什麼意思?一成不變的意思,所以 Immutable List 就是一個不可變的 List 類,這意味著該 List 聲明後,它的內容就是固定的,不可增刪改的。
如果對不可變類比較陌生的話,可以先點擊下面的鏈接查看我之前寫的另外一篇文章。
如果嘗試對 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,都是不可修改的。
public static <T> List<T> unmodifiableList(List<? extends T> list) {
return (list instanceof RandomAccess ?
new Collections.UnmodifiableRandomAccessList<>(list) :
new Collections.UnmodifiableList<>(list));
}
來看一下使用方法:
List<String> list = new ArrayList<>(Arrays.asList("沉默王二", "沉默王三", "沉默王四"));
List<String> unmodifiableList = Collections.unmodifiableList(list);
我們嘗試往 unmodifiableList 中添加元素「沉默王五」:
unmodifiableList.add("沉默王五");
運行後會拋出 UnsupportedOperationException 異常:
Exception in thread "main" java.lang.UnsupportedOperationException
at java.base/java.util.Collections$UnmodifiableCollection.add(Collections.java:1062)
at com.cmower.mkyong.immutablelist.ImmutableListDemo.main(ImmutableListDemo.java:16)
02、藉助 Java 9
Java 9 的時候,List 類新增了一個 of()
靜態工廠方法,可以用來創建不可變的 List。先來看一下源碼:
static <E> List<E> of(E e1, E e2, E e3) {
return new ImmutableCollections.ListN<>(e1, e2, e3);
}
of()
方法有很多變體,比如說:
static <E> List<E> of(E e1) {
return new ImmutableCollections.List12<>(e1);
}
static <E> List<E> of(E e1, E e2) {
return new ImmutableCollections.List12<>(e1, e2);
}
static <E> List<E> of(E e1, E e2, E e3, E e4) {
return new ImmutableCollections.ListN<>(e1, e2, e3, e4);
}
static <E> List<E> of(E e1, E e2, E e3, E e4, E e5) {
return new ImmutableCollections.ListN<>(e1, e2, e3, e4, e5);
}
static <E> List<E> of(E e1, E e2, E e3, E e4, E e5, E e6, E e7, E e8, E e9, E e10) {
return new ImmutableCollections.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) { // implicit null check of elements
case 0:
@SuppressWarnings("unchecked")
var list = (List<E>) ImmutableCollections.ListN.EMPTY_LIST;
return list;
case 1:
return new ImmutableCollections.List12<>(elements[0]);
case 2:
return new ImmutableCollections.List12<>(elements[0], elements[1]);
default:
return new ImmutableCollections.ListN<>(elements);
}
}
不管是 ImmutableCollections.List12 還是 ImmutableCollections.ListN,它們都是 final 的,並且繼承了 AbstractImmutableList,裡面的元素也是 final 的。
static final class List12<E> extends ImmutableCollections.AbstractImmutableList<E>
implements Serializable {
@Stable
private final E e0;
@Stable
private final E e1;
}
static final class ListN<E> extends ImmutableCollections.AbstractImmutableList<E>
implements Serializable {
// EMPTY_LIST may be initialized from the CDS archive.
static @Stable List<?> EMPTY_LIST;
static {
VM.initializeFromArchive(ImmutableCollections.ListN.class);
if (EMPTY_LIST == null) {
EMPTY_LIST = new ImmutableCollections.ListN<>();
}
}
@Stable
private final E[] elements;
}
好了,來看一下使用方法吧:
final List<String> unmodifiableList = List.of("沉默王二", "沉默王三", "沉默王四");
unmodifiableList.add("沉默王五");
ImmutableCollections 的內部類 ListN 或者 List12 同樣不可修改,使用 add()
方法添加元素同樣會在運行時拋出異常:
Exception in thread "main" java.lang.UnsupportedOperationException
at java.base/java.util.ImmutableCollections.uoe(ImmutableCollections.java:73)
at java.base/java.util.ImmutableCollections$AbstractImmutableCollection.add(ImmutableCollections.java:77)
at com.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're overriding default serialization
public abstract class ImmutableList<E> extends ImmutableCollection<E>
implements List<E>, RandomAccess {
}
它的類結構關係如下所示:
java.lang.Object
↳ java.util.AbstractCollection
↳ com.google.common.collect.ImmutableCollection
↳ com.google.common.collect.ImmutableList
ImmutableList 類的 copyOf()
方法可用於創建一個不可變的 List 對象:
List<String> list = new ArrayList<>(Arrays.asList("沉默王二", "沉默王三", "沉默王四"));
List<String> unmodifiableList = ImmutableList.copyOf(list);
unmodifiableList.add("沉默王五");
ImmutableList 同樣不允許添加元素,add()
方法在執行的時候會拋出 UnsupportedOperationException 異常:
Exception in thread "main" java.lang.UnsupportedOperationException
at com.google.common.collect.ImmutableCollection.add(ImmutableCollection.java:244)
at com.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 = new ArrayList<>();
list.add("沉默王二");
List<String> iList = Collections.unmodifiableList(list);
list.add("沉默王三");
list.add("沉默王四");
System.out.println(iList);
程式輸出的結果如下所示:
[沉默王二, 沉默王三, 沉默王四]
但如果我們通過 ImmutableList 類創建一個不可變 List,原有 List 的改變並不會影響到 ImmutableList。
List<String> list = new ArrayList<>();
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。