Arrays.asList()、Collections.singletonList()
Arrays.asList(strArray)返回值是仍然是一個可變的集合,但是返回值是其內部類,不具有add方法,可以通過set方法進行增加值,默認長度是10
Collections.singletonList()返回的是不可變的集合,但是這個長度的集合只有1,可以減少內存空間。但是返回的值依然是Collections的內部實現類,同樣沒有add的方法,調用add,set方法會報錯
java.lang.UnsupportedOperationException //調用add()都會報錯
如何將一個元素優雅的轉換成一個集合
List authcChannels = Stream.of(K).collect(Collectors.toList());
Java 8及早期版本
Java 1.3開始就提供下面的靜態工廠方法:
Collections::singletonList.
List list= Collections.singletonList(item);
希望進一步節省代碼的開發者能會想到Arrays::asList工廠方法。這個方法從Java 1.2起就一直存在。
List list= Arrays.asList(item);
然而這種方案並不可取。asList方法只接收varargs參數,這意味着item參數創建列表之前就已經包裝爲數組。用這種方法創建的List類型是ArrayList,這並不奇怪。但實際得到的類型可能不是java.util.ArrayList,而是java.util.Arrays$ArrayList私有類,兩者有以下區別:
沒有實現Cloneable接口(不是很明顯)。
列表底層實際上是傳遞給asList方法的數組。java.util.ArrayList會創建內部數組並對其管理。
不支持java.util.List接口定義的許多方法,尤其是mutation方法。(詳細信息參見下表)
Java 8中Stream API提供了更多創建單項列表的方法,儘管使用的方法更不直觀:
List list= Stream.of(item).collect(Collectors.toList);
List immutableList = Stream.of(item).collect(Collectors.toUnmodifiableList);
無論採用哪種collector,這種方法都不可取。因爲除了創建List之外還創建了一個StreamCollector和Collector。只有前者纔是我們真正唯一關心的事情。
對於Java 8及早期版本Collections::singletonList是單行代碼中創建單元列表最好的辦法。
對於Java 8以後的Java版本還是最佳選擇嗎?
Java 9及更高版本
Java 9中增加了一個很棒的API,List::of。接受一個或多個參數,返回一個包含這些參數的List。乍一看似乎和Arrays::asList沒什麼區別。但是仔細檢查後您會注意到,List::of方法支持不同數量的參數,包括下面討論中的元素:
/**
-
返回包含一個元素的不可變列表
-
更多信息參見 Unmodifiable Lists
-
@param {@code List} 的元素類型
-
@param e1 單個元素
-
@return a {@code List} 包含指定元素
-
@throws NullPointerException 如果元素爲 {@code null}
-
@since 9
*/
static List of(E e1) {
returnnewImmutableCollections.List12<>(e1);
}
List::of支持從一到十個固定數量的命名參數,所有這些都是爲了避免使用varargs數組。如您所料,餘下的List::of方法接受varargs參數,處理極少數情況下列表中包含11個甚至個更多item。
List::of與Collections::singletonList相比,哪個更勝一籌?
Collections::singletonList vs. List::of
可以從多個角度比較這兩個工廠。
編程效率:方法各自適用什麼場景?
代碼可讀性:使用方法對代碼可讀性有什麼影響?
List API支持:方法返回的List實例支持哪些操作?
Null支持:哪個方法支持null?
內存使用:方法返回的List實例消耗多少內存?
性能:每種方法創建List的效率如何?
編程效率
與Collections.singletonList相比,List.of代碼更少。導入java.util.Collections不需要輸入(或者用IDE快捷方式)。由於需要處理List返回值,通常已經導入了java.util.List。
此外,如果需要增加item,只要向List::of中加入參數,無需改用其他方法。
代碼可讀性
儘管Collections::singletonList明確表明返回的列表僅包含一個item,但List.of(item)的返回值也很清楚:“返回包含此item的一個列表。”在我看來,這樣讀起來很自然。
實際上,結果是一個list這個事實比列表中只有一個item更重要。List::of突出了這個信息,而Collections::singletonList一直看到最後四個字母才解除了我們的疑惑。
List API支持
每個工廠方法返回的java.util.List實現各有不同,並且支持的API方法之間存在細微差異。Collections::singletonList返回java.util.CollectionsList12支持更多操作。
爲了比較,下面的列表中還包含了之前提到的java.util.Arrays$ArrayList和java.util.ArrayList,後面是Collectors.toList的返回類型。Collectors.toUnmodifiableList與List::of返回的列表類型相同
圖例:
✔️ 表示支持該方法
❌表示調用此方法會拋出UnsupportedOperationException;
❗️表示僅在方法參數不會引起mutation的情況下才支持該方法,例如Collections.singletonList(“foo”).retainAll(“foo”)沒問題,但Collections.singletonList(“foo”).retainAll(“bar”)會拋出UnsupportedOperationException
從不變性角度考慮List::of的ImmutableCollections.List12最強;不論傳入的參數如何,每個方法都會拋出UnsupportedOperationException。
Collections::singletonList儘管允許調用一些“mutator”方法,但最終結果還是不可變的。
Arrays::asList 返回值類型是可變的;可以修改返回值(同時會更改傳給工廠方法的數組值),但不能添加或刪除item調整大小。
有趣的是,java.util.CollectionsSingletonList並不完全符合java.util.List規範。
ArrayList和LinkedList也有類似問題。List::sort的JavaDocs聲稱:“如果指定的comparator參數爲null,則列表中所有元素都必須實現Comparable接口”。真的是這樣嗎?那爲什麼下面這段代碼能正常工作呢?
List list= newArrayList<>;
list.add( newObject); // java.lang.Object 並沒有實現 Comparable 接口
list.sort(null); // 沒有拋出 java.lang.ClassCastException
Null支持
如果出於某些奇怪的原因打算創建包含一個null元素的單元列表,那麼不能使用List:of,NullPointerException會映入你眼簾(沒錯,NullPointerException可以用作動詞)。Array::asList和基於Stream的方法也是如此。
Collections::singletonList支持創建null列表。
內存使用
使用jcmd爲一個簡單程序生成了GC.class_histogram。這個該程序使用Collections::singletonList與List::of各創建了十萬個列表。
#instances #bytes class name (module)
1000772401848java.util.ImmutableCollections$List12 (java.base@ 12.0.2)
1000002400000java.util.Collections$SingletonList (java.base@ 12.0.2)
我不太確定java.util.ImmutableCollections$List12多出來的77個實例來自哪裏,但是用字節數除以實例個數會發現,每個類實例恰好佔用了24個字節。鑑於每個list都包含對一個item引用,這很有意義。64位JVM上的每個類佔用12個字節(禁止壓縮OOP),每個引用消耗8個字節,總共20個字節。當填充到接近8的倍數時,會達到24個字節。
性能
使用JMH創建一個基準測試,檢測目前爲止使用上述所有方法創建列表的平均時間和吞吐量:
基準模式Cnt得分誤差單位
Approach.collectionsSingletonList thrpt 5154.848± 16.030ops/us
Approach.listOf thrpt 5147.524± 10.477ops/us
Approach.arraysAsList thrpt 590.731± 2.655ops/us
Approach.streamAndCollectToList thrpt 54.481± 0.459ops/us
Approach.streamAndCollectToUnmodifiableList thrpt 54.235± 0.081ops/us
Approach.collectionsSingletonList avgt 50.006± 0.001us/op
Approach.listOf avgt 50.007± 0.001us/op
Approach.arraysAsList avgt 50.011± 0.001us/op
Approach.streamAndCollectToList avgt 50.217± 0.004us/op
Approach.streamAndCollectToUnmodifiableList avgt 50.241± 0.036us/op
根據這些數據,Collections::singletonList的吞吐量比List::of略高且平均執行速度略快,但是性能基本相同。
下一個Arrays::asList,速度大約是它的兩倍,吞吐量是它的60%。相比之下,Stream API提供的兩種方法測試結果非常糟糕。
爲什麼Collections::singletonList的性能要比List::of的性能略好一些?我猜測唯一的可能是java.util.ImmutableCollections.List12調用了Objects::requireNonNull強制保證“不允許傳入null”。就像前面提到的那樣,java.util.Collections$SingletonList支持null(無論好壞),因此不對constructor的參數進行任何檢查。
總結
Collections::singletonList和List::of都是創建單元列表的絕佳選擇。如果使用的Java版本支持這兩種方法(Java 9及更高版本),我建議使用List:of,因爲它使用方便、代碼可讀性強且不可變性更好