[Java] JDK 9 新变化之 Convenience Factory Methods for Collections

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时,
List.of(...) 返回 List12 的实例") node12("List12 中用 e0, e1 两个字段
来保存所有的元素") ListN node21("参数中的元素数是0或大于等于3时,
List.of(...) 返回 ListN 的实例") ListN 使用 elements 字段来保存所有的元素

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

mindmap Root("Set.of(...)") Set12 node11("参数中的元素数是1或2时,
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(...) 的思维导图如下 ⬇️

mindmap Root("Map.of(...)/Map.ofEntries(...)") Map1 node11("待处理的 key-value pair 的个数是1时,
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 pairMap 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 pairMap 的话,将会很方便。

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$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 <> AbstractCollection <> AbstractImmutableCollection <> AbstractImmutableList <> AbstractImmutableSet <> AbstractMap <> AbstractImmutableMap <> Iterable <> Collection <> SequencedCollection <> List <> RandomAccess <> Set <> Map
在上图中的类名/接口名 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'

参考资料

相关推荐
微小冷2 小时前
Rust图形界面教程:egui基础组件的使用
后端·rust·gui·egui·button·panel·用户图形界面
javadaydayup2 小时前
同样是简化代码,Lambda 和匿名内部类的核心原理是什么?
后端
用户7406696136252 小时前
入门并理解Java模块化系统(JPMS)
java
Yeats_Liao2 小时前
时序数据库系列(六):物联网监控系统实战
数据库·后端·物联网·时序数据库
金銀銅鐵2 小时前
[Java] 用 Swing 生成一个最大公约数计算器
java·后端
brzhang2 小时前
我觉得可以试试 TOON —— 一个为 LLM 而生的极致压缩数据格式
前端·后端·架构
小安同学iter2 小时前
SQL50+Hot100系列(11.7)
java·算法·leetcode·hot100·sql50
苏三的开发日记2 小时前
库存预扣减之后,用户订单超时之后补偿库存的方案
后端
yivifu3 小时前
JavaScript Selection API详解
java·前端·javascript