Arrays.asList使用指南
- 2019 年 10 月 7 日
- 筆記
一、java.util.Arrays.asList() 的一般用法
List是一種很有用的數據結構,如果需要將一個數組轉換為 List 以便進行更豐富的操作的話,可以這麼實現:

或

上面這兩種形式都是十分常見的:將需要轉化的數組作為參數,或者直接把數組元素作為參數,都可以實現轉換。
二、極易出現的錯誤及相應的解決方案
錯誤一: 將原生數據類型數據的數組作為參數
前面說過,可以將需要轉換的數組作為 asList 方法的參數。假設現在需要轉換一個整型數組,那麼我們可能會這樣寫:

上面這段程式碼的輸出結果是什麼,會是3嗎?如果有人自然而然地寫出上面這段程式碼的話,那麼他也一定會以為 myList 的大小為3。很遺憾,這段程式碼的輸出結果不是3,而是1。如果嘗試遍歷 myList ,你會發現得到的元素不是1、2、3中的任意一個,而是一個帶有 hashCode 的對象。為什麼會如此?
來看一下asList 方法的簽名:

注意:參數類型是 T ,根據官方文檔的描述,T 是數組元素的 class。
如果你對反射技術比較了解的話,那麼 class 的含義想必是不言自明。我們知道任何類型的對象都有一個 class 屬性,這個屬性代表了這個類型本身。原生數據類型,比如 int,short,long等,是沒有這個屬性的,具有 class 屬性的是它們所對應的包裝類 Integer,Short,Long。
因此,這個錯誤產生的原因可解釋為:asList 方法的參數必須是對象或者對象數組,而原生數據類型不是對象——這也正是包裝類出現的一個主要原因。當傳入一個原生數據類型數組時,asList 的真正得到的參數就不是數組中的元素,而是數組對象本身!此時List 的唯一元素就是這個數組。
解決方案:使用包裝類數組
如果需要將一個整型數組轉換為 List,那麼就將數組的類型聲明為 Integer 而不是 int。

這時 myList 的大小就是3了,遍歷的話就得到1、2、3。這種方案是比較簡潔明了的。
另一種解決方案——他使用了 Java 8 新引入的 API:

錯誤二:試圖修改 List 的大小
我們知道 List 是可以動態擴容的,因此在創建一個 List 之後最常見的操作就是向其中添加新的元素或是從裡面刪除已有元素:

嘗試運行這段程式碼,結果拋出了一個 java.lang.UnsupportedOperationException 異常!這一異常意味著,向 phoneList 添加新元素是不被允許的;如果試圖從 phoneList 中刪除元素,也會拋出相同的異常。為什麼會如此?
用 asList 方法產生的 List 是固定大小的,這也就意味著任何改變其大小的操作都是不允許的。
那麼新的問題來了:按道理 List 本就支援動態擴容,那為什麼偏偏 asList 方法產生的 List 就是固定大小的呢?如果要回答這一問題,就需要查看相關的源碼。Java 8 中 asList 方法的源碼如下:

方法中的的確確生成了一個ArrayList ,這不應該是支援動態擴容的嗎?別著急,接著往下看。緊跟在 asList 方法後面,有這樣一個內部類:

這個內部類也叫 ArrayList,更重要的是在這個內部類中有一個被聲明為 final 的數組 a,所有傳入的元素都會被保存在這個數組a中。到此,謎底又揭曉了: asList 方法返回的確實是一個 ArrayList ,但這個 ArrayList 並不是java.util.ArrayList,而是 java.util.Arrays的一個內部類。這個內部類用一個 final 數組來保存元素,因此用 asList 方法產生的 ArrayList是不可修改大小的。
解決方案:創建一個真正的 ArrayList
既然我們已經知道之所以asList 方法產生的 ArrayList 不能修改大小,是因為這個 ArrayList 並不是「貨真價實」的 ArrayList ,那我們就自行創建一個真正的 ArrayList :

在上面這段程式碼中,我們 new 了一個 java.util.ArrayList ,然後再把 asList 方法的返回值作為構造器的參數傳入,最後得到的 myList 自然就是可以動態擴容的了。
三、用自己的方法實現數組到 List 的轉換
有時,自己實現一個方法要比使用庫中的方法好。鑒於 asList 方法有一些限制,那麼我們可以用自己的方法來實現數組到 List 的轉換:

這麼做自然也是可以達到目的的,但顯然有一個缺點:程式碼相對冗長。
老九學堂會員社群出品
作者:naaman