How can Java stream reduce accumulator parameter be associative

一笑奈何 提交于 2020-06-25 05:54:36

问题


The question is about

java.util.stream.Stream.reduce(U identity,
             BiFunction<U, ? super T, U> accumulator,
             BinaryOperator<U> combiner) 

method.

From the API docs:

accumulator – an associative, non-interfering, stateless function for incorporating an additional element into a result

Now, the definition of associative is:

(a op b) op c = a op (b op c) for all a, b, c

As we can see, the above definition requires a BinaryOperator, not only a BiFunction, because a op b requires b of type T while b op c requires b of type U. If U and T are distinct, this is impossible.


My question

How can the definition of associative be formulated for a BiFunction that is not a BinaryOperator?


UPDATE

I have submitted a bug to Oracle about this issue, with the internal review ID : 9063337

I will update the question with Oracle's feedback.


回答1:


Mind the documentation:

Additionally, the combiner function must be compatible with the accumulator function; for all u and t, the following must hold:

combiner.apply(u, accumulator.apply(identity, t)) == accumulator.apply(u, t)

whereas == is not to be taken literally but rather means “equivalent result”

So in fact, we have a much stronger constraint on accumulator than associativity, as it must be compatible with the combiner, which is an associative function.

There’s also the API Note:

Many reductions using this form can be represented more simply by an explicit combination of map and reduce operations. The accumulator function acts as a fused mapper and accumulator, which can sometimes be more efficient than separate mapping and reduction, …

When the accumulator function is a fused mapping and reduction function, only the reduction part must be associative. When you want to test that part, just test whether the combiner function fulfills the associativity constraint and whether combiner and accumulator are compatible in the specified way. If both are proven, the associativity of the accumulator’s reduction part has been proven too.

While the API Note says “many reductions using this form”, actually all of them can be expressed as an explicit combination of map and reduce operations, when we ignore the efficiency, due to the specified constraints:

Function<T,U> mappingFunc = t -> accumulator.apply(identitiy, t);
U result = streamOfT.map(mappingFunc).reduce(identitiy, combiner);

This result must be equivalent to the result of, e.g. a typical sequential evaluation of streamOfT.reduce(identitiy, accumulator, combiner), which doesn’t use the combiner at all.

We can formulate narrow test cases for the associativity as

U u1 = accumulator.apply(accumulator.apply(identitiy, t1), t2);
U u2 = combiner.apply(accumulator.apply(identitiy, t1), accumulator.apply(identitiy, t2));

where u1 and u2 must be equivalent results, as well as

U u1 = accumulator.apply(accumulator.apply(accumulator.apply(identitiy, t1), t2), t3);
U u2 = combiner.apply(accumulator.apply(identitiy, t1),
           accumulator.apply(accumulator.apply(identitiy, t2), t3));


来源:https://stackoverflow.com/questions/59782474/how-can-java-stream-reduce-accumulator-parameter-be-associative

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