Java 创建不可变集合,用户希望在 Java 9 中创建不可变列表、集合或映射,可以使用 Java 9 新增的静态工厂方法 List.of
、Set.of
与 Map.of
。根据 Javadoc 的描述,可以使用 Java 9 引入的各种 List.of()
静态工厂方法方便地创建不可变列表。通过这些方法创建的 List
实例具有以下特征。
Java 创建不可变集合 问题描述
用户希望在 Java 9 中创建不可变列表、集合或映射。
Java 创建不可变集合 解决方案
使用 Java 9 新增的静态工厂方法 List.of
、Set.of
与 Map.of
。
Java 创建不可变集合 具体实例
根据 Javadoc 的描述,可以使用 Java 9 引入的各种 List.of()
静态工厂方法方便地创建不可变列表。通过这些方法创建的 List
实例具有以下特征。
- 结构上是不可变的(structurally immutable),即无法执行添加、删除或替换元素的操作,且调用任何更改器方法(mutator method)都会抛出
UnsupportedOperationException
。然而,如果包含的元素本身是可变的,则可能导致List
的内容发生变化。 - 禁止使用
null
元素,尝试创建包含null
元素的List
实例将抛出NullPointerException
。 - 如果所有元素是可序列化的(serializable),则
List
实例也是可序列化的。 - 列表中元素的顺序与所提供参数的顺序相同,与所提供数组中元素的顺序也相同。
- 根据 Serialized Form 页面的规定进行序列化。
例 10-10 列出了 List.of
方法所有可用的重载形式。
例 10-10 用于创建不可变列表的静态工厂方法
正如 Javadoc 指出的那样,通过上述方法创建的列表在结构上是不可变的,因此无法在 List
上调用任何常规的更改器方法。换言之,调用 add
、addAll
、clear
、remove
、removeAll
、replaceAll
以及 set
都会抛出 UnsupportedOperationException
。例 10-11 显示了部分测试用例。
例 10-11 不可变列表的应用
然而,如果列表包含的对象本身是可变的,那么列表也可能发生变化。为说明这个问题,我们创建一个名为 Holder
的类。这个类很简单,它包含一个可变值 x
,如例 10-12 所示。
例 10-12 包含可变值的简单类
如果创建一个 Holder
实例的不可变列表,由于 Holder
包含的值是可变的,列表也会发生变化,如例 10-13 所示。
例 10-13 修改包装的整数
❶ Holder
实例的不可变列表
❷ 修改 Holder
中包含的值
虽然上述代码可以运行且不违反文档规定,但有悖于开发所应遵循的最佳实践。换言之,如果计划使用不可变列表,应尽量在列表中包含不可变对象。
类似地,根据 Javadoc 的描述,可以使用各种 Set.of()
方法方便地创建不可变集合。通过这些方法创建的 Set
实例具有以下特征。
- 创建时(creation time)拒绝重复元素,传递给静态工厂方法的重复元素会导致
IllegalArgumentException
。 - 未指定集合元素的迭代顺序,因此它可能会发生变化。
所有 Set.of()
方法的签名与对应的 List.of()
方法相同,只不过返回的是 Set<E>
。
Map.of()
方法同样如此,但其签名传入交替的键和值作为参数,如例 10-14 所示。
例 10-14 用于创建不可变映射的静态工厂方法
在创建包含最多 10 个条目的映射时,虽然可以使用相应的 Map.of()
方法(交替传入键和值),不过这并非最佳方案,因此 Map
接口还定义了另外两种静态方法,即 ofEntries
和 entry
:
例 10-15 显示了如何利用上面介绍的各种方法创建不可变映射。
例 10-15 利用各种方法创建不可变映射
❶ 使用 Map.ofEntries
方法
❷ 使用 Map.of
方法
可以看到,将 ofEntries
与 entry
方法结合在一起使用有助于简化代码。