为什么这样deviseJava 8'Collector'类?

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

但是,收集器接口是这样devise的:

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

为什么它不是像下面这样devise的?

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

后者更容易实现。 devise它作为前者的考虑是什么?

其实它最初的devise与你的build议类似。 查看项目lambda库中的早期实现 ( makeResult现在是supplier )。 后来更新到现在的devise。 我相信,这种更新的基本原理是简化收集器组合器。 我没有find关于这个主题的任何具体的讨论,但是我的猜测是由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()); } 

这个实现只需要重新定义accumulatorfunction,就可以让suppliercombinerfinisher原样,所以在调用suppliercombinerfinisher combiner时不需要额外的间接finisher :您只需直接调用原始收集器返回的function即可。 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); } 

这里只有finisher被改变,但是使用原始的supplieraccumulatorcombiner 。 由于accumulator被称为每个元素,减less间接性可能是非常重要的。 尝试重写mappingcollectingAndThen用您提出的devise,你会看到这个问题。 新的JDK-9收集器,如filteringflatMapping也受益于当前的devise。

构成比inheritance更受青睐。

你问题中的第一种模式是模块configuration。 收集器接口的实现可以为供应商,累加器等提供不同的实现。这意味着可以从现有的供应商,累加器等实现中组成收集器实现。 这也有助于重复使用,两个收集器可能使用相同的累加器实现。 Stream.collect()使用提供的行为。

在第二种模式中,收集器实现必须自己实现所有的function。 各种各样的变化将需要覆盖父执行。 重复使用的范围不大,加上代码重复,如果两个收集器有一个步骤类似的逻辑,例如积累。

2相关的原因

  • function组合通过组合。 (注意你仍然可以做OO构成但是看下面的点)
  • 当分配目标是一个function接口时,可以通过lambdaexpression式或方法引用将业务逻辑组织成简洁的expression式代码

    function组成

    Collectors API通过组合器为function组合创build了一条途径.ie构build了小/可重用的小function,并将其中的一些经常以一种有趣的方式结合到高级特性/function中。

    简洁的expression代码

    下面我们使用函数指针(Employee :: getSalary)来将Mapper的function从Employee对象填充到int。 summmingInt填补了添加ints的逻辑,因此合并在一起,我们就得到了一行写出来的声明代码。

    //计算员工工资总和int total = employees.stream().collect(Collectors.summingInt( Employee :: getSalary )));