JDK 9 新变化之 Convenience Factory Methods for Collections
背景
以下方法都是 JDK 9 中新增的。
List.of(...)(这里用...表示我们不关心参数的类型/数量,下同)Set.of(...)Map.of(...)Map.ofEntries(...)
详细的描述可以参考 JEP 269: Convenience Factory Methods for Collections(本文的很多内容都参考了它)。在本文中,我会在 JEP 269: Convenience Factory Methods for Collections 的基础上,谈谈自己的理解。
要点
以下方法都是 JDK 9 中新增的。有了这些方法,我们可以方便地创建包含少量元素(或者 key-value pair)的 List/Set/Map。这些方法调用起来很直观,而且它们底层的结构也比较简单。
List.of(...)Set.of(...)Map.of(...)Map.ofEntries(...)
List.of(...) 的思维导图如下 ⬇️
List.of(...) 返回 List12 的实例") node12("List12 中用 e0, e1 两个字段
来保存所有的元素") ListN node21("参数中的元素数是0或大于等于3时,
List.of(...) 返回 ListN 的实例") ListN 使用 elements 字段来保存所有的元素
Set.of(...) 的思维导图如下 ⬇️
Set.of(...) 返回 Set12 的实例") node12("Set12 使用 e0, e1 两个字段
来保存所有的元素") SetN node21("参数中的元素数是0或大于等于3时,
Set.of(...) 返回 SetN 的实例") node22("SetN 使用 elements 字段来保存所有的元素") node23("SetN 使用 开放寻址法(open addressing)
来访问元素")
Map.of(...)/Map.ofEntries(...) 的思维导图如下 ⬇️
Map.of(...)/Map.ofEntries(...) 返回 Map1 的实例") node12("Map1 使用 k0, v0 两个字段
来保存仅有的 key-value pair") MapN node21("待处理的 key-value pair 的个数不等于1时,
Map.of(...)/Map.ofEntries(...) 返回 MapN 的实例") node22("MapN 使用 table 字段来保存所有的 key 和 value") node23("MapN 使用 开放寻址法(open addressing)
来访问 key-value pair")
正文
过去的做法
在 JDK 9 之前,如果我们想创建一个只包含 "a", "b", "c" 3 个元素的 set,可能会采用以下几种做法(这几种做法的代码均来自 JEP 269: Convenience Factory Methods for Collections)。
做法一
java
Set<String> set = new HashSet<>();
set.add("a");
set.add("b");
set.add("c");
set = Collections.unmodifiableSet(set);
如果是对静态字段赋值,需要将这样的代码写在静态初始化块里(因为这样的代码没法在一个表达式里写完)。
做法二
java
Set<String> set = Collections.unmodifiableSet(new HashSet<>(Arrays.asList("a", "b", "c")));
第二种做法会额外创建 List 对象。
做法三
java
Set<String> set = Collections.unmodifiableSet(new HashSet<String>() {{
add("a"); add("b"); add("c");
}});
第三种做法利用了匿名内部类。
做法四
java
Set<String> set = Collections.unmodifiableSet(Stream.of("a", "b", "c").collect(toSet()));
第四种做法利用了 Java 8 提供的 Stream API。
以上四种做法都包含了不少代码,我们只是想创建一个小的 set 而已,如果可以不用写这么多代码就好了。
过去如何创建空的 List/Set/Map?
java.util.Collections 类中在更早的时候已经提供了相关的方法 ⬇️
调用 java.util.Collections 类中的哪个静态方法? |
得到的是哪个类的实例? | |
|---|---|---|
如何创建空的 List |
Collections.emptyList() |
java.util.Collections$EmptyList |
如何创建空的 Set |
Collections.emptySet() |
java.util.Collections$EmptySet |
如何创建空的 Map |
Collections.emptyMap() |
java.util.Collections$EmptyMap |
过去如何创建单元素(或一个 key-value pair)的 List/Set/Map?
java.util.Collections 类中在更早的时候已经提供了相关的方法 ⬇️
调用 java.util.Collections 类中的哪个静态方法? |
得到的是哪个类的实例? | |
|---|---|---|
如何创建单元素的 List |
Collections.singletonList(T) |
java.util.Collections$SingletonList |
如何创建单元素的 Set |
Collections.singleton(T) |
java.util.Collections$SingletonSet |
如何创建只包含一个 key-value pair 的 Map |
Collections.singletonMap(K, V) |
java.util.Collections$SingletonMap |
JDK 9
如果在 List/Set/Map 中可以提供一些静态方法,让我们可以创建含有任意多个元素(或 key-value pair)的 List/Set/Map 就好了。例如对 List/Set 而言,我们希望可以写出如下的代码。
java
List.of(a, b, c);
Set.of(d, e, f, g);
如果 List.of(...)/Set.of(...) 系列的方法中有支持变长参数的的版本,那么我们就可以为任意数量的元素生成对应的 List/Set。
对 Map 而言,如果可以用如下的代码来生成包含少量 key-value pair 的 Map 的话,将会很方便。
java
Map.of()
Map.of(k1, v1)
Map.of(k1, v1, k2, v2)
Map.of(k1, v1, k2, v2, k3, v3)
...
如果 key-value pair 的数量比较多的话,那么 Map.of(...) 将无法支持(因为 JVM 中对方法的参数个数的上限有约束)。此时,如果有如下的方法,那么也算差强人意。
java
Map.ofEntries(Map.Entry<K,V>...)
我们可以这样调用 ⬇️
java
Map.ofEntries(
Map.entry(k1, v1),
Map.entry(k2, v2),
Map.entry(k3, v3),
// ... entry(kn, vn));
JDK 9 中确实新增了这些方法 ⬇️
List.of(...)Set.of(...)Map.of(...)Map.ofEntries(...)
List.of(...) 返回的是以下两个类的实例 ⬇️
java.util.ImmutableCollections$List12java.util.ImmutableCollections$ListN
Set.of(...) 返回的是以下两个类的实例 ⬇️ (更多细节可以参考我写的另一篇文章:[Java] 浅析 Set.of(...) 方法)
java.util.ImmutableCollections$Set12java.util.ImmutableCollections$SetN
Map.of(...)/Map.ofEntries(...) 返回的是以下两个类的实例 ⬇️ (更多细节可以参考我写的另一篇文章:[Java] 浅析 Map.of(...) 方法和 Map.ofEntries(...) 方法)
java.util.ImmutableCollections$Map1java.util.ImmutableCollections$MapN
其中
List12/Set12/Map1中用字段来保存元素(或者key-valuepair)List12使用e0,e1两个字段来保存所有元素Set12使用e0,e1两个字段来保存所有元素Map1使用k0, v0两个字段来保存仅有的那个key-valuepair
ListN/SetN/MapN中用数组来保存元素(或者key-valuepair)ListN使用elements字段来保存所有元素SetN使用elements字段来保存所有元素,并通过 开放寻址法(open addressing) 来访问它们MapN使用table字段来保存所有的key和value,并通过 开放寻址法(open addressing) 来访问它们
List12/ListN/Set12/SetN/Map1/MapN 的类图如下 ⬇️
请注意:以下的类/接口在类图中被忽略了
java.io.Serializable
| 在上图中的类名/接口名 | Fully Qualified Name |
|---|---|
AbstractCollection |
java.util.AbstractCollection |
AbstractImmutableCollection |
java.util.ImmutableCollections$AbstractImmutableCollection |
AbstractImmutableList |
java.util.ImmutableCollections$AbstractImmutableList |
AbstractImmutableMap |
java.util.ImmutableCollections$AbstractImmutableMap |
AbstractImmutableSet |
java.util.ImmutableCollections$AbstractImmutableSet |
AbstractMap |
java.util.AbstractMap |
Collection |
java.util.Collection |
Iterable |
java.lang.Iterable |
List |
java.util.List |
List12 |
java.util.ImmutableCollections$List12 |
ListN |
java.util.ImmutableCollections$ListN |
Map |
java.util.Map |
Map1 |
java.util.ImmutableCollections$Map1 |
MapN |
java.util.ImmutableCollections$MapN |
RandomAccess |
java.util.RandomAccess |
SequencedCollection |
java.util.SequencedCollection |
Set |
java.util.Set |
Set12 |
java.util.ImmutableCollections$Set12 |
SetN |
java.util.ImmutableCollections$SetN |
其他
文中的类图是如何绘制的?
我在 [Java] 如何自动生成简单的 Mermaid 类图 一文中分享过可以生成简单 Mermaid 类图的代码,在那些代码的基础上,可以通过执行如下的命令来生成类图 ⬇️
bash
java ClassDiagramGenerator -i 'java.io.Serializable' 'java.util.ImmutableCollections$List12' 'java.util.ImmutableCollections$ListN' 'java.util.ImmutableCollections$Set12' 'java.util.ImmutableCollections$SetN' 'java.util.ImmutableCollections$Map1' 'java.util.ImmutableCollections$MapN'