popcorny popcorny - 1 year ago 48
Java Question

Why is the Java 8 'Collector' class designed in this way?

We know Java 8 introduces a new Stream API and

is the interface to define how to aggregate/collect the data stream.

However, the Collector interface is designed like this:

public interface Collector<T, A, R> {
Supplier<A> supplier();
BiConsumer<A, T> accumulator();
BinaryOperator<A> combiner();
Function<A, R> finisher();

Why is it not designed like the following?

public interface Collector<T, A, R> {
A supply();
void accumulate(A accumulator, T value);
A combine(A left, A right);
R finish(A accumulator);

The latter one is much easier to implement. What were the consideration to design it as the former one?

Answer Source

Actually it was originally designed similarly to what you propose. See the early implementation in project lambda repository (makeResult is now supplier). It was later updated to the current design. I believe, the rationale of such update is to simplify collector combinators. I did not find any specific discussion on this topic, but my guess is supported by the fact that mapping collector appeared in the same changeset. Consider the implementation of 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(),

This implementation needs to redefine accumulator function only, leaving supplier, combiner and finisher as is, so you don't have additional indirection when calling supplier, combiner or finisher: you just call directly the functions returned by the original collector. It's even more important with 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(),

Here only finisher is changed, but original supplier, accumulator and combiner are used. As accumulator is called for every element, reducing the indirection could be pretty important. Try to rewrite mapping and collectingAndThen with your proposed design and you will see the problem. New JDK-9 collectors like filtering and flatMapping also benefit from current design.