I have a List
of A, B, C.
C reduce A reduce B != A reduce B reduce C
(however, A reduce (B reduce C) is OK).
In other words, my reduction operation is associative but not commutative.
Does java enforce on an ordered sequential stream (such as the default one from a list) that reduction will always happen according to the encounter order? That is to say, will java reorder reductions (such that B reduce A instead of A reduce B)?
(Hopefully this is clear enough).
edit to add a little demo and maybe helping to clarify
Stream.of(" cats ", " eat ", " bats ")
.reduce("", (a, b) -> a + b); // cats eat bats
With the above, could the output ever be "bats cats eat" or "eat bats cats"? Is that guaranteed somewhere in the spec?
What concerns me is this blurb in the reduce docs "This is equivalent to ... but is not constrained to execute sequentially."
Execution order is not the same as encounter order. The stream pipeline is allowed to perform "cats" + ("eat" + "bats")
or ("cats" + "eat") + "bats"
According to the specification it respects the order of the elements.
A proof is very simple. The specification claims that a reduction function has to be associative.
However, associativity it self doesn't make any sense if the order is not preserved. According to the mathematical definition of the associative property:
Within an expression containing two or more occurrences in a row of the same associative operator, the order in which the operations are performed does not matter as long as the sequence of the operands is not changed.
In other words, associative property doesn't imply that:
(a + b) + c = (a + c) + b
It only allows an arbitrary permutation of the order in which operations are applied.
You have asked two questions in one.
- Does java enforce on an ordered sequential stream (such as the default one from a list) that reduction will always happen according to the encounter order?
Assuming “will always happen” is referring to the order of the function evaluation, the answer is no, this is not guaranteed.
With the above, could the output ever be "bats cats eat" or "eat bats cats"? Is that guaranteed somewhere in the spec?Stream.of(" cats ", " eat ", " bats ") .reduce("", (a, b) -> a + b); // cats eat bats
Regardless of the evaluation order of the reduction function (the processing order), the result is guaranteed to be " cats eat bats "
, correctly reflecting the encounter order (see also this answer). To ensure that the unspecified processing order still yields the correct result regarding the encounter order, the reduction function must be associative, as specified
Note that the documentation even shows .reduce("", String::concat)
as an example of a valid, but inefficient reduction function. Similarly, (a,b) -> b
has been acknowledged as a valid way to get the last element of a stream.
The key point is given in the “Associativity” section of the documentation:
Associativity
An operator or function
op
is associative if the following holds:(a op b) op c == a op (b op c)
The importance of this to parallel evaluation can be seen if we expand this to four terms:
a op b op c op d == (a op b) op (c op d)
So we can evaluate
(a op b)
in parallel with(c op d)
, and then invokeop
on the results.Examples of associative operations include numeric addition, min, and max, and string concatenation.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With