为什么收集器接口的组合器与重载的收集方法不一致?

时间:2015-05-18 17:10:23

标签: java java-8 java-stream

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

<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声明:

  

组合器函数可以将状态从一个参数折叠到另一个参数并返回该参数,或者可以返回一个新的结果容器。

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

3 个答案:

答案 0 :(得分:18)

The "inline" (3-arg) version of collect is designed for when you already have these functions "lying around". For example:

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

Or

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

While these are just motivating examples, our explorations with similar existing builder classes was that the signatures of the existing combiner candidates were more suited to conversion to BiConsumer than to BinaryOperator. Offering the "flexibility" you ask for would make this overload far less useful in the very cases it was designed to support -- which is when you've already got the functions lying around, and you don't want to have to make (or learn about making) a Collector just to collect them.

Collector, on the other hand, has a far wider range of uses, and so merits the additional flexibility.

答案 1 :(得分:3)

请注意Stream.collect()的主要目的是支持Mutable Reduction。对于此操作,累加器组合器这两个函数都是为了操作可变容器而不需要返回值。

因此,不坚持返回值会更方便。作为Brian Goetz has pointed out,此决定允许重用大量现有容器类型及其方法。如果没有直接使用这些类型的能力,整个三参数collect方法将毫无意义。

相比之下,Collector接口是此操作的抽象,支持更多用例。最值得注意的是,您甚至可以使用值类型(或具有值类型语义的类型)Reduction来建模普通的,即非可变的via a Collector操作。在这种情况下,必须是返回值,因为不能修改值对象本身。

当然,它不能用作stream.collect(Collectors.reducing(…))而不是stream.reduce(…)。相反,当组合收集器时,这种抽象会很方便。比如groupingBy(…,reducing(…))

答案 2 :(得分:2)

如果以前的collect方法收到BinaryOperator<R>,则以下示例将无法编译:

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

在这种情况下,编译器无法推断combiner的返回类型,并且会产生编译错误。

因此,如果此版本的collect方法与Collector接口一致,那么它会促使更复杂地使用此版本的collect方法,而这种方法并非如此。