Board logo

标题: 使用流执行聚合(3) [打印本页]

作者: look_w    时间: 2018-6-24 14:48     标题: 使用流执行聚合(3)

自定义收集器尽管 JDK 提供的标准的收集器集合非常大,但编写您自己的收集器非常容易。Collector 接口(如清单 4                所示)非常简单。该接口通过 3 种类型来实现参数化:输入类型 T、累加器类型 A                和最终的返回类型 R(A 和 R                通常是相同的),这些方法返回的函数与之前演示的 collect() 3 参数版本所接受的函数类似。
清单 4. Collector                接口
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public interface Collector<T, A, R> {
    /** Return a function that creates a new empty result container */
    Supplier<A> supplier();

    /** Return a function that incorporates an element into a container */
    BiConsumer<A, T> accumulator();

    /** Return a function that merges two result containers */
    BinaryOperator<A> combiner();

    /** Return a function that converts the intermediate result container
        into the final representation */
    Function<A, R> finisher();

    /** Special characteristics of this collector */
    Set<Characteristics> characteristics();
}




Collectors 中的大部分收集器工厂的实现都很简单。例如,toList()                的实现是:
1
2
3
4
return new CollectorImpl<>(ArrayList::new,
                           List::add,
                           (left, right) -> { left.addAll(right); return left; },
                           CH_ID);




此实现使用 ArrayList 作为结果容器,使用 add() 合并一个元素,并使用                addAll() 将一个列表合并到另一个中,通过这些特征表明它的完成函数是身份函数(这使得流框架可以优化执行)。
与之前看到的一样,一些一致性要求与缩减中的身份和累加器函数之间的限制类似。这些要求已在 Collector                的规范中列出。
作为一个更复杂的示例,可以考虑在数据集上创建汇总统计数据的问题。很容易使用缩减来计算数字数据集的总和、最小值、最大值或数量(而且您可以使用总和和数量来计算平均值)。在数据上,使用缩减在一轮计算中一次性计算所有这些结果更加困难。但您可以轻松地编写一个                Collector 来高效地(如果愿意,还可并行地)执行此计算。
Collectors 类包含一个 collectingInt() 工厂方法,该方法返回一个                IntSummaryStatistics,后者会执行您想要的准确操作,比如在一轮计算中计算                sum、min、max、count 和                average。IntSummaryStatistics                的实现很简单,而且您可以轻松地编写自己的类似收集器来计算任意数据汇总结果(或扩展此结果)。
清单 5 显示了 IntSummaryStatistics 类。实际实现包含更多细节(包含用于获取汇总统计数据的                getter),但它的核心是简单的 accept() 和 combine() 方法。
清单 5. summarizingInt() 收集器使用的                    IntSummaryStatistics                类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class IntSummaryStatistics implements IntConsumer {
    private long count;
    private long sum;
    private int min = Integer.MAX_VALUE;
    private int max = Integer.MIN_VALUE;

    public void accept(int value) {
        ++count;
        sum += value;
        min = Math.min(min, value);
        max = Math.max(max, value);
    }

    public void combine(IntSummaryStatistics other) {
        count += other.count;
        sum += other.sum;
        min = Math.min(min, other.min);
        max = Math.max(max, other.max);
    }

    // plus getters for count, sum, min, max, and average
}




如您所见,这是一个非常简单的类。在观察每个新数据元素时,会以各种方式更新各种汇总结果,而且会以各种方式组合两个                IntSummaryStatistics                持有者。Collectors.summarizingInt() 的实现(如清单 6 所示)同样很简单;它创建了一个                Collector,以便通过应用一个整数值来提取器函数,并将结果传递给                IntSummaryStatistics.accept() 来合并一个元素。
清单 6. summarizingInt()                收集器工厂
1
2
3
4
5
6
7
8
public static <T>
Collector<T, ?, IntSummaryStatistics> summarizingInt(ToIntFunction<? super T> mapper) {
    return new CollectorImpl<T, IntSummaryStatistics, IntSummaryStatistics>(
            IntSummaryStatistics::new,
            (r, t) -> r.accept(mapper.applyAsInt(t)),
            (l, r) -> { l.combine(r); return l; },
            CH_ID);
}




组合收集器的容易性(您在 groupingBy()                示例中已看到)和创建新收集器的容易性相结合,可以创建流数据的几乎任何汇总结果,同时保持您的代码紧凑而又清晰。




欢迎光临 电子技术论坛_中国专业的电子工程师学习交流社区-中电网技术论坛 (http://bbs.eccn.com/) Powered by Discuz! 7.0.0