问题
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
combinerfunction must be compatible with theaccumulatorfunction; for alluandt, 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