为什么Java 8'Collector'类以这种方式设计?

pop*_*rny 34 java java-8 java-stream collectors

我们知道Java 8引入了一个新的Stream API,并且java.util.stream.Collector是定义如何聚合/收集数据流的接口.

但是,Collector接口的设计如下:

public interface Collector<T, A, R> {
    Supplier<A> supplier();
    BiConsumer<A, T> accumulator();
    BinaryOperator<A> combiner();
    Function<A, R> finisher();
}
Run Code Online (Sandbox Code Playgroud)

为什么它的设计不符合以下规定?

public interface Collector<T, A, R> {
    A supply();
    void accumulate(A accumulator, T value);
    A combine(A left, A right);
    R finish(A accumulator);
}
Run Code Online (Sandbox Code Playgroud)

后者更容易实现.将它设计为前者的考虑是什么?

Tag*_*eev 26

实际上它最初设计的类似于你的建议.查看项目lambda存储库中的早期实现(makeResult现在是supplier).后来更新到了当前的设计.我相信,这种更新的基本原理是简化收集器组合器.我没有找到关于这个主题的具体讨论,但我的猜测得到了mapping收集器出现在同一变更集中的事实的支持.考虑实施Collectors.mapping:

public static <T, U, A, R>
Collector<T, ?, R> mapping(Function<? super T, ? extends U> mapper,
                           Collector<? super U, A, R> downstream) {
    BiConsumer<A, ? super U> downstreamAccumulator = downstream.accumulator();
    return new CollectorImpl<>(downstream.supplier(),
                               (r, t) -> downstreamAccumulator.accept(r, mapper.apply(t)),
                               downstream.combiner(), downstream.finisher(),
                               downstream.characteristics());
}
Run Code Online (Sandbox Code Playgroud)

此实现只需重新定义accumulator函数,离开supplier,combiner并且finisher因此在调用时没有额外的间接supplier,combiner或者finisher:您只需直接调用原始收集器返回的函数.它更重要的是collectingAndThen:

public static<T,A,R,RR> Collector<T,A,RR> collectingAndThen(Collector<T,A,R> downstream,
                                                            Function<R,RR> finisher) {
    // ... some characteristics transformations ...
    return new CollectorImpl<>(downstream.supplier(),
                               downstream.accumulator(),
                               downstream.combiner(),
                               downstream.finisher().andThen(finisher),
                               characteristics);
}
Run Code Online (Sandbox Code Playgroud)

这里只finisher改变了,但是原创的supplier,accumulator并且combiner被使用了.正如accumulator每个元素所要求的那样,减少间接可能非常重要.尝试重写mappingcollectingAndThen使用您提出的设计,您将看到问题.新的JDK-9收藏家喜欢filtering并且flatMapping也受益于当前的设计.


S.D*_*.D. 18

组合比继承更受青睐.

您问题中的第一个模式是模块配置.Collector接口的实现可以为Supplier,Accumulator等提供不同的实现.这意味着可以从现有的Supplier,Accumulator等实现池中组合 Collector实现.这也有助于重用,两个收集器可能使用相同的Accumulator实现.在Stream.collect()使用提供的行为.

在第二种模式中,收集器实现必须自己实现所有功能.各种变体都需要覆盖父实现.如果两个收集器具有类似于步骤的逻辑,例如累积,则重用的范围不大,加上代码重复.