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(...) 的思维导图如下 ⬇️

mindmap
      Root("List.of(...)")
          List12
              node11("参数中的元素数是1或2时,<br/>List.of(...) 返回 List12 的实例")
              node12("List12 中用 e0, e1 两个字段<br/>来保存所有的元素")
          ListN
            node21("参数中的元素数是0或大于等于3时,<br/>List.of(...) 返回 ListN 的实例")
            ListN 使用 elements 字段来保存所有的元素

Set.of(...) 的思维导图如下 ⬇️

mindmap
      Root("Set.of(...)")
          Set12
              node11("参数中的元素数是1或2时,<br/>Set.of(...) 返回 Set12 的实例")
              node12("Set12 使用 e0, e1 两个字段<br/>来保存所有的元素")
          SetN
            node21("参数中的元素数是0或大于等于3时,<br/>Set.of(...) 返回 SetN 的实例")
            node22("SetN 使用 elements 字段来保存所有的元素")
            node23("SetN 使用 开放寻址法(open addressing)<br/>来访问元素")

Map.of(...)/Map.ofEntries(...) 的思维导图如下 ⬇️

mindmap
      Root("Map.of(...)/Map.ofEntries(...)")
          Map1
            node11("待处理的 key-value pair 的个数是1时,<br/>Map.of(...)/Map.ofEntries(...) 返回 Map1 的实例")
            node12("Map1 使用 k0, v0 两个字段<br/>来保存仅有的 key-value pair")
          MapN
            node21("待处理的 key-value pair 的个数不等于1时,<br/>Map.of(...)/Map.ofEntries(...) 返回 MapN 的实例")
            node22("MapN 使用 table 字段来保存所有的 key 和 value")
            node23("MapN 使用 开放寻址法(open addressing)<br/>来访问 key-value pair")

正文

过去的做法

JDK 9 之前,如果我们想创建一个只包含 "a", "b", "c" 3 个元素的 set,可能会采用以下几种做法(这几种做法的代码均来自 JEP 269: Convenience Factory Methods for Collections)。

做法一
Set<String> set = new HashSet<>(); 
set.add("a"); 
set.add("b"); 
set.add("c"); 
set = Collections.unmodifiableSet(set);

如果是对静态字段赋值,需要将这样的代码写在静态初始化块里(因为这样的代码没法在一个表达式里写完)。

做法二
Set<String> set = Collections.unmodifiableSet(new HashSet<>(Arrays.asList("a", "b", "c")));

第二种做法会额外创建 List 对象。

做法三
Set<String> set = Collections.unmodifiableSet(new HashSet<String>() {{ 
    add("a"); add("b"); add("c"); 
}});

第三种做法利用了匿名内部类。

做法四
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 类中的哪个静态方法?得到的是哪个类的实例?
如何创建空的 ListCollections.emptyList()java.util.Collections$EmptyList
如何创建空的 SetCollections.emptySet()java.util.Collections$EmptySet
如何创建空的 MapCollections.emptyMap()java.util.Collections$EmptyMap

过去如何创建单元素(或一个 key-value pair)的 List/Set/Map

java.util.Collections 类中在更早的时候已经提供了相关的方法 ⬇️

调用 java.util.Collections 类中的哪个静态方法?得到的是哪个类的实例?
如何创建单元素的 ListCollections.singletonList(T)java.util.Collections$SingletonList
如何创建单元素的 SetCollections.singleton(T)java.util.Collections$SingletonSet
如何创建只包含一个 key-value pairMapCollections.singletonMap(K, V)java.util.Collections$SingletonMap

JDK 9

如果在 List/Set/Map 中可以提供一些静态方法,让我们可以创建含有任意多个元素(或 key-value pair)的 List/Set/Map 就好了。例如对 List/Set 而言,我们希望可以写出如下的代码。

List.of(a, b, c); 
Set.of(d, e, f, g);

如果 List.of(...)/Set.of(...) 系列的方法中有支持变长参数的的版本,那么我们就可以为任意数量的元素生成对应的 List/Set

Map 而言,如果可以用如下的代码来生成包含少量 key-value pairMap 的话,将会很方便。

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 中对方法的参数个数的上限有约束)。此时,如果有如下的方法,那么也算差强人意。

Map.ofEntries(Map.Entry<K,V>...)

我们可以这样调用 ⬇️

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$List12
  • java.util.ImmutableCollections$ListN

Set.of(...) 返回的是以下两个类的实例 ⬇️ (更多细节可以参考我写的另一篇文章:[Java] 浅析 Set.of(...) 方法)

  • java.util.ImmutableCollections$Set12
  • java.util.ImmutableCollections$SetN

Map.of(...)/Map.ofEntries(...) 返回的是以下两个类的实例 ⬇️ (更多细节可以参考我写的另一篇文章:[Java] 浅析 Map.of(...) 方法和 Map.ofEntries(...) 方法)

  • java.util.ImmutableCollections$Map1
  • java.util.ImmutableCollections$MapN

其中

  • List12/Set12/Map1 中用字段来保存元素(或者 key-value pair
    • List12 使用 e0, e1 两个字段来保存所有元素
    • Set12 使用 e0, e1 两个字段来保存所有元素
    • Map1 使用 k0, v0 两个字段来保存仅有的那个 key-value pair
  • ListN/SetN/MapN 中用数组来保存元素(或者 key-value pair
    • ListN 使用 elements 字段来保存所有元素
    • SetN 使用 elements 字段来保存所有元素,并通过 开放寻址法(open addressing) 来访问它们
    • MapN 使用 table 字段来保存所有的 keyvalue,并通过 开放寻址法(open addressing) 来访问它们

List12/ListN/Set12/SetN/Map1/MapN 的类图如下 ⬇️

请注意:以下的类/接口在类图中被忽略了

  • java.io.Serializable
classDiagram

Iterable <|-- Collection
Collection <|.. AbstractCollection
AbstractCollection <|-- AbstractImmutableCollection
Collection <|-- SequencedCollection
SequencedCollection <|-- List
AbstractImmutableCollection <|-- AbstractImmutableList
List <|.. AbstractImmutableList
RandomAccess <|.. AbstractImmutableList
AbstractImmutableList <|-- List12
AbstractImmutableList <|-- ListN
Collection <|-- Set
AbstractImmutableCollection <|-- AbstractImmutableSet
Set <|.. AbstractImmutableSet
AbstractImmutableSet <|-- Set12
AbstractImmutableSet <|-- SetN
Map <|.. AbstractMap
AbstractMap <|-- AbstractImmutableMap
AbstractImmutableMap <|-- Map1
AbstractImmutableMap <|-- MapN

<<Abstract>> AbstractCollection
<<Abstract>> AbstractImmutableCollection
<<Abstract>> AbstractImmutableList
<<Abstract>> AbstractImmutableSet
<<Abstract>> AbstractMap
<<Abstract>> AbstractImmutableMap
<<interface>> Iterable
<<interface>> Collection
<<interface>> SequencedCollection
<<interface>> List
<<interface>> RandomAccess
<<interface>> Set
<<interface>> Map
在上图中的类名/接口名Fully Qualified Name
AbstractCollectionjava.util.AbstractCollection
AbstractImmutableCollectionjava.util.ImmutableCollections$AbstractImmutableCollection
AbstractImmutableListjava.util.ImmutableCollections$AbstractImmutableList
AbstractImmutableMapjava.util.ImmutableCollections$AbstractImmutableMap
AbstractImmutableSetjava.util.ImmutableCollections$AbstractImmutableSet
AbstractMapjava.util.AbstractMap
Collectionjava.util.Collection
Iterablejava.lang.Iterable
Listjava.util.List
List12java.util.ImmutableCollections$List12
ListNjava.util.ImmutableCollections$ListN
Mapjava.util.Map
Map1java.util.ImmutableCollections$Map1
MapNjava.util.ImmutableCollections$MapN
RandomAccessjava.util.RandomAccess
SequencedCollectionjava.util.SequencedCollection
Setjava.util.Set
Set12java.util.ImmutableCollections$Set12
SetNjava.util.ImmutableCollections$SetN

其他

文中的类图是如何绘制的?

我在 [Java] 如何自动生成简单的 Mermaid 类图 一文中分享过可以生成简单 Mermaid 类图的代码,在那些代码的基础上,可以通过执行如下的命令来生成类图 ⬇️

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'

参考资料

  • JEP 269: Convenience Factory Methods for Collections
  • [Java] 浅析 Set.of(...) 方法
  • [Java] 浅析 Map.of(...) 方法和 Map.ofEntries(...) 方法
  • [Java] 如何自动生成简单的 Mermaid 类图
本站提供的所有下载资源均来自互联网,仅提供学习交流使用,版权归原作者所有。如需商业使用,请联系原作者获得授权。 如您发现有涉嫌侵权的内容,请联系我们 邮箱:[email protected]