Java 实现Collector接口

Java 实现Collector接口,由于 java.util.stream.Collectors 类提供的工厂方法无法满足需要,用户希望手动实现 java.util.stream.Collector 接口。为工厂方法 Collector.of 传入的 supplieraccumulatorcombinerfinisher 函数提供 lambda 表达式或方法引用,以及其他所需的特性。

Java 实现Collector接口 问题描述

由于 java.util.stream.Collectors 类提供的工厂方法无法满足需要,用户希望手动实现 java.util.stream.Collector 接口。

Java 实现Collector接口 解决方案

为工厂方法 Collector.of 传入的 supplieraccumulatorcombinerfinisher 函数提供 lambda 表达式或方法引用,以及其他所需的特性。

Java 实现Collector接口 具体实例

Collectors 工具类定义了多种便利的静态方法,它们的返回类型为 Collector。这些方法包括 toListtoSettoMap 以及 toCollection,相关讨论请参见其他章节。实现 Collector 的类的实例作为 Stream.collect 方法的参数。如例 4-34 所示,evenLengthStrings 方法传入字符串参数,并返回仅包含偶数长度字符串的 List

例 4-34 利用 collect 方法返回 List

public List<String> evenLengthStrings(String... strings) {
    return Stream.of(strings)
        .filter(s -> s.length() % 2 == 0)
        .collect(Collectors.toList());  ➊
}

➊ 将偶数长度的字符串收集到 List
编写自定义收集器的过程则略显复杂。收集器使用 5 个函数,它们的作用是将条目累加到可变容器,并有选择性地对结果进行转换。这 5 个函数是 supplieraccumulatorcombinerfinisher 以及 characteristics
我们首先讨论 characteristics 函数,表示 Collector.Characteristics 枚举的一个不可变的元素 Set。三个枚举常量为 CONCURRENTIDENTITY_FINISHUNORDEREDCONCURRENT 表示结果容器支持多个线程在结果容器上并发地调用累加器函数,UNORDERED 表示集合操作无须保留元素的出现顺序(encounter order),IDENTITY_FINISH 表示终止器函数返回其参数而不做任何修改。
请注意,如果默认值就是实际需要的,则不必提供任何特性。下面列出了每种参数的用途。
supplier()
  使用 Supplier<A> 创建累加器容器(accumulator container)。
accumulator()
  使用 BiConsumer<A,T> 为累加器容器添加一个新的数据元素。
combiner()
  使用 BinaryOperator<A> 合并两个累加器容器。
finisher()
  使用 Function<A,R> 将累加器容器转换为结果容器。
characteristics()
  从枚举值中选择的 Set<Collector.Characteristics>
如果读者熟悉 java.util.function 包定义的函数式接口,则不难理解各个参数的含义: Supplier 用于创建累加临时结果所用的容器;BiConsumer 用于将一个元素添加到累加器; BinaryOperator 表示输入类型和输出类型相同,因此可以将两个累加器合二为一;最后,Function 将累加器转换为所需的结果容器。
程序在收集过程中调用上述方法,它们由 Stream.collect 这样的方法触发。从概念上讲,集合过程相当于例 4-35 所示的(泛型)代码(取自 Javadoc)。

例 4-35 各种 Collector 方法的用法

R container = collector.supplier.get();           ➊
for (T t : data) {
    collector.accumulator().accept(container, t); ➋
}
return collector.finisher().apply(container);     ➌

❶ 创建累加器容器
❷ 将每个元素添加到累加器容器
❸ 通过 finisher 函数将累加器容器转换为结果容器
本例并未出现 combiner 函数,这或许令人感到困惑。如果处理的是顺序流,则不需要该函数,算法将既定方式执行。如果处理的是并行流,流将被分为多个子流,每个子流都会生成各自的累加器容器。接下来,在连接过程中使用 combiner 函数将多个累加器容器合并为一个,然后应用 finisher 函数。
例 4-36 显示了与例 4-34 类似的代码。

例 4-36 利用 collect 方法返回不可修改的 SortedSet

public SortedSet<String> oddLengthStringSet(String... strings) {
        Collector<String, ?, SortedSet<String>> intoSet =
                Collector.of(TreeSet<String>::new,           ➊
                        SortedSet::add,                      ➋
                        (left, right) -> {                   ➌
                              left.addAll(right);
                              return left;
                        },
                        Collections::unmodifiableSortedSet); ➍
        return Stream.of(strings)
                .filter(s -> s.length() % 2 != 0)
                .collect(intoSet);
    }

Supplier:创建新的 TreeSet
BiConsumer:将每个字符串添加到 TreeSet
BinaryOperator:将两个 SortedSet 实例合二为一
finisher:创建不可修改的 Set
程序将输出一个经过排序且不可修改的字符串集,它按字典序排序。
本例展示了如何通过 Collector.of 方法生成收集器。of 方法包括以下两种形式:

static <T,A,R> Collector<T,A,R> of(Supplier<A> supplier,
    BiConsumer<A,T> accumulator,
    BinaryOperator<A> combiner,
    Function<A,R> finisher,
    Collector.Characteristics... characteristics)

static <T,R> Collector<T,R,R>    of(Supplier<R> supplier,
    BiConsumer<R,T> accumulator,
    BinaryOperator<R> combiner,
    Collector.Characteristics... characteristics)

Collectors 类提供了多种用于生成收集器的便利方法,用户几乎不需要创建自定义收集器,不过掌握相关的知识仍然很有必要。综合应用 java.util.function 包定义的函数式接口,可以创建各种有趣的对象。

赞(2)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

Java 实例