为什么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。对于此操作,accumulator 和 combiner 这两个函数都用于操作可变容器,不需要 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(…))
.
接口 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。对于此操作,accumulator 和 combiner 这两个函数都用于操作可变容器,不需要 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(…))
.