Java集合的便利工廠方法
1.概述
Java 9帶來了期待已久的語法糖,它使用簡潔的單行代碼創建了小的不可修改的Collection實例。根據JEP 269 ,JDK 9中將包括新的便利工廠方法。
在本文中,我們將介紹其用法以及實現細節。
2.歷史與動機
使用傳統方式在Java中創建一個小型的不可變Collection非常冗長。
讓我們以Set為例:
Set<String> set = new HashSet<>();
set.add("foo");
set.add("bar");
set.add("baz");
set = Collections.unmodifiableSet(set);
這對於一個簡單的任務來說太多了代碼,應該可以在一個表達式中完成。
以上對於地圖也適用。
但是,對於List ,有一個工廠方法:
List<String> list = Arrays.asList("foo", "bar", "baz");
儘管創建List的方法比構造方法的初始化要好,但這並不那麼明顯,因為通常的直覺是不要在Arrays類中查找創建List的方法:
還有其他降低冗長程度的方法,例如雙括號初始化技術:
Set<String> set = Collections.unmodifiableSet(new HashSet<String>() {{
add("foo"); add("bar"); add("baz");
}});
或使用Java 8 Streams :
Stream.of("foo", "bar", "baz")
.collect(collectingAndThen(toSet(), Collections::unmodifiableSet));
雙括號技術僅稍微冗長一些,但大大降低了可讀性(被認為是反模式)。
但是,Java 8版本是單行表達式,它也存在一些問題。首先,它並不明顯和直觀。其次,它仍然很冗長。第三,它涉及不必要對象的創建。第四,該方法不能用於創建Map 。
總結缺點,以上方法均未處理特定用例,從而產生了一個很小的,不可修改的Collection一流類問題。
3.描述和用法
為List , Set和Map接口提供了靜態方法,這些方法將元素作為參數並分別返回List , Set和Map的實例。
對於所有三個接口,此方法都被稱為of(...) 。
3.1。清單和設定
List和Set工廠方法的簽名和特徵相同:
static <E> List<E> of(E e1, E e2, E e3)
static <E> Set<E> of(E e1, E e2, E e3)
方法的用法:
List<String> list = List.of("foo", "bar", "baz");
Set<String> set = Set.of("foo", "bar", "baz");
如我們所見,它非常簡單,簡短和簡潔。
在示例中,我們使用的方法正好將三個元素作為參數,並返回大小為3的列表/集合。
但是,此方法有12個重載版本–十一個參數為0到10,而一個參數為var-args:
static <E> List<E> of()
static <E> List<E> of(E e1)
static <E> List<E> of(E e1, E e2)
// ....and so on
static <E> List<E> of(E... elems)
對於大多數實際目的,10個元素就足夠了,但是如果需要更多元素,則可以使用var-args版本。
現在,我們可能會問,如果有一個適用於任意數量元素的var-args版本,那麼擁有11個額外方法的意義何在?
答案就是性能。每個var-args方法調用都會隱式創建一個數組。使用重載的方法可以避免不必要的對象創建及其垃圾回收開銷。相反, Arrays.asList始終創建該隱式數組,因此,當元素數較少時,效率較低。
在使用工廠方法創建Set的過程中,如果將重複的元素作為參數傳遞,則在運行時會拋出IllegalArgumentException :
@Test(expected = IllegalArgumentException.class)
public void onDuplicateElem_IfIllegalArgExp_thenSuccess() {
Set.of("foo", "bar", "baz", "foo");
}
這裡要注意的重要一點是,由於工廠方法使用泛型,因此原始類型會自動裝箱。
如果傳遞原始類型的數組,則返回該原始類型的數組的列表。
例如:
int[] arr = { 1, 2, 3, 4, 5 };
List<int[]> list = List.of(arr);
在這種情況下,將返回大小為1的List <int []> ,並且索引0處的元素包含該數組。
3.2。地圖
Map工廠方法的簽名為:
static <K,V> Map<K,V> of(K k1, V v1, K k2, V v2, K k3, V v3)
和用法:
Map<String, String> map = Map.of("foo", "a", "bar", "b", "baz", "c");
與List和Set相似, of(...)方法被重載為具有0到10個鍵值對。
對於Map ,對於10個以上的鍵/值對有一種不同的方法:
static <K,V> Map<K,V> ofEntries(Map.Entry<? extends K,? extends V>... entries)
它的用法:
Map<String, String> map = Map.ofEntries(
new AbstractMap.SimpleEntry<>("foo", "a"),
new AbstractMap.SimpleEntry<>("bar", "b"),
new AbstractMap.SimpleEntry<>("baz", "c"));
為Key傳遞重複值將拋出IllegalArgumentException :
@Test(expected = IllegalArgumentException.class)
public void givenDuplicateKeys_ifIllegalArgExp_thenSuccess() {
Map.of("foo", "a", "foo", "b");
}
同樣,在Map的情況下,原始類型也會自動裝箱。
4.實施說明
使用工廠方法創建的集合不是常用的實現。
例如, List不是ArrayList ,而Map不是HashMap 。這些是Java 9中引入的不同實現。這些實現是內部的,其構造函數具有受限制的訪問權限。
在本節中,我們將看到一些重要的實現差異,這是所有三種類型的集合所共有的。
4.1。一成不變的
使用工廠方法創建的集合是不可變的,並且更改元素,添加新元素或刪除元素會拋出UnsupportedOperationException :
@Test(expected = UnsupportedOperationException.class)
public void onElemAdd_ifUnSupportedOpExpnThrown_thenSuccess() {
Set<String> set = Set.of("foo", "bar");
set.add("baz");
}
@Test(expected = UnsupportedOperationException.class)
public void onElemModify_ifUnSupportedOpExpnThrown_thenSuccess() {
List<String> list = List.of("foo", "bar");
list.set(0, "baz");
}
另一方面,從Arrays.asList返回的集合 是可變的。因此,可以更改或刪除現有元素。類似List.of,我們不能將新元素添加到從Arrays.asList返回一個列表。
@Test(expected = UnsupportedOperationException.class)
public void onElemRemove_ifUnSupportedOpExpnThrown_thenSuccess() {
Map<String, String> map = Map.of("foo", "a", "bar", "b");
map.remove("foo");
}
4.2。不允許空元素
對於List和Set ,任何元素都不能為null 。對於Map ,鍵或值都不能為null 。傳遞null參數會引發NullPointerException :
@Test(expected = NullPointerException.class)
public void onNullElem_ifNullPtrExpnThrown_thenSuccess() {
List.of("foo", "bar", null);
}
與List.of相反, Arrays.asList方法接受空值。
4.3。基於價值的實例
通過工廠方法創建的實例是基於值的。這意味著工廠可以自由創建新實例或返回現有實例。
因此,如果我們創建具有相同值的List,則它們可能會或可能不會引用堆上的同一對象:
List<String> list1 = List.of("foo", "bar");
List<String> list2 = List.of("foo", "bar");
在這種情況下,取決於JVM, list1 == list2可能會或可能不會為true 。
4.4。序列化
如果集合的元素是可序列化的,則從工廠方法創建的集合是可序列化的。
5.結論
在本文中,我們介紹了Java 9中引入的Collections的新工廠方法。
通過回顧一些過去創建不可修改集合的方法,我們總結了為什麼此功能是受歡迎的更改。我們介紹了它的用法,並突出顯示了在使用它們時要考慮的關鍵點。
最後,我們澄清了這些集合與常用的實現方式不同,並指出了關鍵的區別。