为什么Collector接口的combiner和重载的collect方法不一致?

Why is the combiner of the Collector interface not consistent with the overloaded collect method?

接口 Stream<T> 中有一个重载方法 collect(),具有以下签名:

<R> R collect(Supplier<R> supplier,
          BiConsumer<R,? super T> accumulator,
          BiConsumer<R,R> combiner)

还有一个版本的collect(Collector<? super T,A,R> collector),它接收一个具有前面三个函数的对象。接口 Collector 对应 combiner 的 属性 具有签名 BinaryOperator<A> combiner().

在后一种情况下,Java API 8 指出:

The combiner function may fold state from one argument into the other and return that, or may return a new result container.

为什么以前的 collect 方法也没有收到 BinaryOperator<R>

collect 的 "inline" (3-arg) 版本专为您已经拥有这些功能 "lying around" 的情况而设计。例如:

ArrayList<Foo> list = stream.collect(ArrayList::new, 
                                     ArrayList::add,
                                     ArrayList::addAll);

或者

BitSet bitset = stream.collect(BitSet::new, 
                               BitSet::set,
                               BitSet::or);

虽然这些只是激励示例,但我们对类似现有构建器的探索 类 是现有组合器候选者的签名更适合转换为 BiConsumer 而不是 BinaryOperator。提供您要求的 "flexibility" 会使此重载在它旨在支持的情况下变得非常有用 - 这是当您已经拥有这些功能并且您不想必须制作(或学习制作)收集器只是为了收集它们。

另一方面,Collector 的用途广泛得多,因此值得额外的灵活性。

如果前一个 collect 方法收到一个 BinaryOperator<R> 那么下面的例子将不会编译:

ArrayList<Foo> list = stream.collect(ArrayList::new, 
                                     ArrayList::add,
                                     ArrayList::addAll);

在这种情况下,编译器无法推断出 combiner 的 return 类型,并且会给出编译错误。

因此,如果此版本的 collect 方法与 Collector 接口一致,那么它会促进此版本的 collect 方法的更复杂使用,这不是预期的。

请记住,Stream.collect() 的主要目的是支持 Mutable Reduction。对于此操作,accumulatorcombiner 这两个函数都用于操作可变容器,不需要 return一个值。

因此,不坚持 return 一个值要方便得多。作为 ,此决定允许重用许多现有的容器类型及其方法。如果不能直接使用这些类型,整个三参数 collect 方法将毫无意义。

相比之下,Collector 接口是此操作的抽象,支持更多用例。最值得注意的是,您甚至可以对普通的(即非可变的)Reduction operation with value types (or types having value type semantics) via a Collector 进行建模。在这种情况下,必须是一个return值,因为值对象本身不能被修改。

当然,它并不意味着用作stream.collect(Collectors.reducing(…))而不是stream.reduce(…)。相反,这种抽象在 组合 收集器时会派上用场,例如像 groupingBy(…,reducing(…)).