Java 创建不可变集合

Java 创建不可变集合,用户希望利用 Stream API 创建不可变的列表、集合或映射。使用 Collectors 类新增的静态方法 collectingAndThen。函数式编程强调并行(parallelization)以及语义的清晰,倾向于尽可能使用不可变对象。

Java 创建不可变集合 问题描述

用户希望利用 Stream API 创建不可变的列表、集合或映射。

Java 创建不可变集合 解决方案

使用 Collectors 类新增的静态方法 collectingAndThen

Java 创建不可变集合 具体实例

函数式编程强调并行(parallelization)以及语义的清晰,倾向于尽可能使用不可变对象。Java 1.2 引入了集合框架,提供以现有集合为基础创建不可变集合的各种方法,但使用起来有些不便。
Collections 工具类定义了 unmodifiableListunmodifiableSetunmodifiableMap 方法(以及其他以 unmodifiable 为前缀的方法),如例 4-30 所示。

例 4-30 Collections 类定义的以 unmodifiable 为前缀的方法

static <T> List<T>    unmodifiableList(List<? extends T> list)
static <T> Set<T>     unmodifiableSet(Set<? extends T> s)
static <K,V> Map<K,V> unmodifiableMap(Map<? extends K,? extends V> m)

上述三种方法的参数分别为现有的列表、集合与映射。结果列表、集合与映射中包含的元素和参数相同,但存在一个重要的区别:所有可以修改集合的方法(如 addremove)现在都能抛出 UnsupportedOperationException
在 Java 8 之前,如果通过可变参数列表获取到单个值作为参数,则会生成一个不可修改的列表或集合,如例 4-31 所示。

例 4-31 创建不可修改的列表或集合(Java 8 之前)

@SafeVarargs   ➊
public final <T> List<T> createImmutableListJava7(T... elements) {
    return Collections.unmodifiableList(Arrays.asList(elements));
}

@SafeVarargs   ➊
public final <T> Set<T> createImmutableSetJava7(T... elements) {
    return Collections.unmodifiableSet(new HashSet<>(Arrays.asList(elements)));
}

➊ 用户承诺不会破坏输入数组类型。详见附录 A。
两段代码首先传入输入值,并将它们转换为 List。第一段代码使用 unmodifiableList 包装生成的列表。而对于 Set,第二段代码先将列表用作集合构造函数的参数,再使用 unmodifiableSet
借由 Java 8 引入的 Stream API,我们可以使用 Collectors 类定义的静态方法 collectingAndThen,如例 4-32 所示。

例 4-32 创建不可修改的列表或集合(Java 8)

import static java.util.stream.Collectors.collectingAndThen;
import static java.util.stream.Collectors.toList;
import static java.util.stream.Collectors.toSet;

// 采用以下方法来定义类

@SafeVarargs
public final <T> List<T> createImmutableList(T... elements) {
    return Arrays.stream(elements)
        .collect(collectingAndThen(toList(),
                    Collections::unmodifiableList));  ➊
}

@SafeVarargs
public final <T> Set<T> createImmutableSet(T... elements) {
    return Arrays.stream(elements)
        .collect(collectingAndThen(toSet(),
                    Collections::unmodifiableSet));   ➊
}

➊ “终止器”对生成的集合进行包装
collectingAndThen 方法传入两个参数,一个是下游 Collector,另一个是称为终止器(finisher)的 Function。该方法的作用是读取输入元素,并将它们收集到 ListSet,然后利用不可修改的函数包装结果集合。
将一系列输入元素转换为一个不可修改的 Map 看起来并不直观,部分原因在于很难看出哪些输入元素将作为键,哪些将作为值。例 4-334 采用实例初始化器(instance initializer),以一种很别扭的方式创建了一个不可变的 Map
4灵感源自 Carl Martensen 的博文“Java 9’s Immutable Collections Are Easier To Create But Use With Caution”。

例 4-33 创建不可变的 Map

Map<String, Integer> map = Collections.unmodifiableMap(
  new HashMap<String, Integer>() {{
    put("have", 1);
    put("the", 2);
    put("high", 3);
    put("ground", 4);
}});

熟悉 Java 9 的读者想必已经了解,本范例中的所有问题都可以通过工厂方法 List.ofSet.ofMap.of 来解决,这些方法能极大提高效率。

Python教程

Java教程

Web教程

数据库教程

图形图像教程

大数据教程

开发工具教程

计算机教程