我们知道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();
}
为什么它的设计不像以下那样?
public interface Collector<T, A, R> {
A supply();
void accumulate(A accumulator, T value);
A combine(A left, A right);
R finish(A accumulator);
}
后者更易于实施。将它设计为前者的考虑是什么?
答案 0 :(得分:26)
实际上它最初的设计与您的建议类似。请参阅项目lambda存储库中的the early implementation(makeResult
现在是supplier
)。它是当前设计的updated。我相信,这种更新的基本原理是简化收集器组合器。我没有找到关于这个主题的任何具体讨论,但我的猜测得到了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());
}
此实现只需重新定义accumulator
函数,保留supplier
,combiner
和finisher
,因此在调用{{1}时没有其他间接功能},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);
}
,但使用了原始finisher
,supplier
和accumulator
。当为每个元素调用combiner
时,减少间接可能非常重要。尝试使用您提出的设计重写accumulator
和mapping
,您将看到问题所在。像collectingAndThen
和filtering
这样的新JDK-9收集器也受益于当前的设计。
答案 1 :(得分:18)
Composition is favored over inheritance.
您问题中的第一个模式是一种模块配置。 Collector接口的实现可以为Supplier,Accumulator等提供不同的实现。这意味着可以从现有的Supplier,Accumulator等实现池构建收集器实现。这也有助于重用,两个收集器可能使用相同的Accumulator实现。 Stream.collect()
使用提供的行为。
在第二种模式中,收集器实现必须自己实现所有功能。各种变体都需要覆盖父实现。如果两个收集器具有类似的步骤逻辑,例如累积,则重用的范围不大,加上代码重复。
答案 2 :(得分:0)
2相关原因
当分配目标是功能界面时,可以通过lambda表达式或方法引用在简洁的富有表现力的代码中构建业务逻辑。
功能组合
Collectors API为组合器的功能组合铺平了道路。构建小/最小的可重用功能,并将其中一些通常以有趣的方式组合到一个高级功能/功能中。
简明扼要的表达代码
下面我们使用函数指针(Employee :: getSalary)将mapper的功能从Employee对象填充到int。 summingInt填充了添加整数的逻辑,因此结合在一起我们在一行声明代码中写出了工资总和。
//计算员工工资总额 int total = employees.stream() .collect(Collectors.summingInt(<强>雇员::的getSalary 强>)));