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

巧了我就是萌 提交于 2019-12-04 15:39:08

问题


There is an overload method, collect(), in interface Stream<T> with the following signature:

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

There is another version of collect(Collector<? super T,A,R> collector), which receives an object with the previous three functions. The property of the interface Collector corresponding to the combiner has the signature BinaryOperator<A> combiner().

In the latter case, the Java API 8 states that:

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

Why does the former collect method not receive a BinaryOperator<R> too?


回答1:


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.




回答2:


Keep in mind that the primary purpose of Stream.collect() is to support Mutable Reduction. For this operation, both functions, the accumulator and the combiner are meant to manipulate the mutable container and don’t need to return a value.

Therefore it is much more convenient not to insist on returning a value. As Brian Goetz has pointed out, this decision allows to re-use a lot of existing container types and their methods. Without the ability to use these types directly, the entire three-arg collect method would be pointless.

In contrast, the Collector interface is an abstraction of this operation supporting much more use cases. Most notably, you can even model the ordinary, i.e. non-mutable, Reduction operation with value types (or types having value type semantics) via a Collector. In this case, there must be a return value as the value objects themselves must not be modified.

Of course, it is not meant to be used as stream.collect(Collectors.reducing(…)) instead of stream.reduce(…). Instead, this abstraction comes handy when combining collectors, e.g. like groupingBy(…,reducing(…)).




回答3:


If the former collect method receive a BinaryOperator<R> then the following example would not compile:

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

In this case the compiler could not infer the return type of the combiner and would give a compilation error.

So if this version of collect method was consistent with the Collector interface then it would promote a more complex use of this version of collect method, that was not intended.



来源:https://stackoverflow.com/questions/30309127/why-is-the-combiner-of-the-collector-interface-not-consistent-with-the-overloade

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!