Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does Java's Stream.reduce method take an identity element instead of a default result?

Stream has three reduce methods:

  1. Optional<T> reduce​(BinaryOperator<T> accumulator)
  2. T reduce​(T identity,BinaryOperator<T> accumulator)
  3. <U> U reduce​(U identity,BiFunction<U,​T,​U> accumulator,BinaryOperator<U> combiner)

Methods 2 and 3 take an identity element.

As explained in Oracles doc on reduction: "The identity element is both the initial value of the reduction and the default result if there are no elements in the stream."

I understand the purpose as a default value: this way the methods do not have to return an Optional. But why is the identity element used as an initial value of the reduction? What is its purpose? Why doesn't it start with first element of the stream as the first reduce method does?

Or asked in another way: why do the second and third reduce methods not take a default value instead of an identity element? This would certainly increase the flexibility because the default value would not have to be the identity value of the operator.

like image 889
Stefan Feuerhahn Avatar asked Oct 20 '25 09:10

Stefan Feuerhahn


1 Answers

Look in particular at the third method: Here the accumulation (wrong word, really; this is a conclist more than a conslist, but I hope you follow the meaning here) is a different type than the stream.

For example, you have a stream of strings, and you reduce it into an op that counts up the string lengths; the accumulation is done as an integer.

In this case, the obvious 'initial / default value' is 0, and you CANT go with the 'first value from the stream'; that is a string and not an int.

Note how this matches up with the types: in the third sig, the type of the initial/default value is 'U', whereas the stream is a stream of T objects.

Even for the first signature, and if the stream isn't empty, you very occasionally don't want to 'just' start with a random (streams are not neccessarily ordered; 'first' doesn't make sense for various streams, so, 'some value' is a better way to think about it than 'the first value', that's already a bit of an issue) value from the stream, but with some known value.

So, let's recap:

  1. If you want to go with 'hey, an arbitrary element from the stream can serve as initial value just fine, BUT, I want to specify a specific default to avoid optional', then that's simple: stream.reduce(accumulatorFunction).orElse(defaultValue) will get you this.

  2. If you want to with 'I have a specific value that serves as initial value', then, I'm having a real tough time imagining how that wouldn't also be the default value if zero elements are in the stream, so, you just use the second form.

  3. If you use a different type as accumulation value vs. what the stream is from, you must use the 3rd form and you must specify an initial value, because an arbitrary item from the stream cannot serve as one; it would have the wrong type. As with the previous: If you specify an explicit initial value, that's.. pretty much always also the proper default value in case of an empty stream.

Thus they all make sense.

like image 93
rzwitserloot Avatar answered Oct 21 '25 21:10

rzwitserloot



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!