How does the reduce() method work in Java 8?In Stream reduce method, must the identity always be 0 for sum and 1 for multiplication?Is Java “pass-by-reference” or “pass-by-value”?How do I efficiently iterate over each entry in a Java Map?Does a finally block always get executed in Java?How does the Java 'for each' loop work?How do I read / convert an InputStream into a String in Java?When to use LinkedList over ArrayList in Java?How do I generate random integers within a specific range in Java?How do I convert a String to an int in Java?Creating a memory leak with JavaJava 8 List<V> into Map<K, V>
Acronyms in HDD specification
Why are solar panels kept tilted?
Offered a new position but unknown about salary?
Why does SSL Labs now consider CBC suites weak?
Is it wrong to omit object pronouns in these sentences?
"The van's really booking"
Is there any good reason to write "it is easy to see"?
How to continually let my readers know what time it is in my story, in an organic way?
Is this possible when it comes to the relations of P, NP, NP-Hard and NP-Complete?
Wireless headphones interfere with Wi-Fi signal on laptop
Why are BJTs common in output stages of power amplifiers?
How do I adjust encounters to challenge my lycanthrope players without negating their cool new abilities?
Is there any way to adjust the damage type of the Eldritch Blast cantrip so that it does fire damage?
Were any toxic metals used in the International Space Station?
Should generated documentation be stored in a Git repository?
Why is it harder to turn a motor/generator with shorted terminals?
How can a layman easily get the consensus view of what academia *thinks* about a subject?
Substring join or additional table, which is faster?
Biology of a Firestarter
Why can't I share a one use code with anyone else?
Is there an academic word that means "to split hairs over"?
Why did the soldiers of the North disobey Jon?
Can my Serbian girlfriend apply for a UK Standard Visitor visa and stay for the whole 6 months?
Was the dragon prowess intentionally downplayed in S08E04?
How does the reduce() method work in Java 8?
In Stream reduce method, must the identity always be 0 for sum and 1 for multiplication?Is Java “pass-by-reference” or “pass-by-value”?How do I efficiently iterate over each entry in a Java Map?Does a finally block always get executed in Java?How does the Java 'for each' loop work?How do I read / convert an InputStream into a String in Java?When to use LinkedList over ArrayList in Java?How do I generate random integers within a specific range in Java?How do I convert a String to an int in Java?Creating a memory leak with JavaJava 8 List<V> into Map<K, V>
.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty,.everyoneloves__bot-mid-leaderboard:empty height:90px;width:728px;box-sizing:border-box;
I try to understand how does the reduce()
method work in java-8.
For example I have this code:
public class App
public static void main(String[] args)
String[] arr = "lorem", "ipsum", "sit", "amet";
List<String> strs = Arrays.asList(arr);
int ijk = strs.stream().reduce(0,
(a, b) ->
System.out.println("Accumulator, a = " + a + ", b = " + b);
return a + b.length();
,
(a, b) ->
System.out.println("Combiner");
return a * b;
);
System.out.println(ijk);
And the output is this:
Accumulator, a = 0, b = lorem
Accumulator, a = 5, b = ipsum
Accumulator, a = 10, b = sit
Accumulator, a = 13, b = amet
17
It is a sum of the length of these strings. And I see that the combiner is not accessed, so it will not multiply the numbers, it only adds the numbers.
But if I have these stream:
int ijk = strs.parallelStream().reduce(0,
(a, b) ->
System.out.println("Accumulator, a = " + a + ", b = " + b);
return a + b.length();
,
(a, b) ->
System.out.println("Combiner");
return a * b;
);
System.out.println(ijk);
This is the output:
Accumulator, a = 0, b = ipsum
Accumulator, a = 0, b = lorem
Accumulator, a = 0, b = sit
Combiner
Accumulator, a = 0, b = amet
Combiner
Combiner
300
I see that the Accumulator and Combiner are accessed both, but only the multiplication is return. So what's happen with the sum?
java java-8 java-stream reduce
add a comment |
I try to understand how does the reduce()
method work in java-8.
For example I have this code:
public class App
public static void main(String[] args)
String[] arr = "lorem", "ipsum", "sit", "amet";
List<String> strs = Arrays.asList(arr);
int ijk = strs.stream().reduce(0,
(a, b) ->
System.out.println("Accumulator, a = " + a + ", b = " + b);
return a + b.length();
,
(a, b) ->
System.out.println("Combiner");
return a * b;
);
System.out.println(ijk);
And the output is this:
Accumulator, a = 0, b = lorem
Accumulator, a = 5, b = ipsum
Accumulator, a = 10, b = sit
Accumulator, a = 13, b = amet
17
It is a sum of the length of these strings. And I see that the combiner is not accessed, so it will not multiply the numbers, it only adds the numbers.
But if I have these stream:
int ijk = strs.parallelStream().reduce(0,
(a, b) ->
System.out.println("Accumulator, a = " + a + ", b = " + b);
return a + b.length();
,
(a, b) ->
System.out.println("Combiner");
return a * b;
);
System.out.println(ijk);
This is the output:
Accumulator, a = 0, b = ipsum
Accumulator, a = 0, b = lorem
Accumulator, a = 0, b = sit
Combiner
Accumulator, a = 0, b = amet
Combiner
Combiner
300
I see that the Accumulator and Combiner are accessed both, but only the multiplication is return. So what's happen with the sum?
java java-8 java-stream reduce
6
combiner
is only called for a parallel stream. also read the documentation of reduce carefully, you can not do summation inaccumulator
and multiplication incombiner
and expect something meaningful as a result
– Eugene
May 3 at 12:50
add a comment |
I try to understand how does the reduce()
method work in java-8.
For example I have this code:
public class App
public static void main(String[] args)
String[] arr = "lorem", "ipsum", "sit", "amet";
List<String> strs = Arrays.asList(arr);
int ijk = strs.stream().reduce(0,
(a, b) ->
System.out.println("Accumulator, a = " + a + ", b = " + b);
return a + b.length();
,
(a, b) ->
System.out.println("Combiner");
return a * b;
);
System.out.println(ijk);
And the output is this:
Accumulator, a = 0, b = lorem
Accumulator, a = 5, b = ipsum
Accumulator, a = 10, b = sit
Accumulator, a = 13, b = amet
17
It is a sum of the length of these strings. And I see that the combiner is not accessed, so it will not multiply the numbers, it only adds the numbers.
But if I have these stream:
int ijk = strs.parallelStream().reduce(0,
(a, b) ->
System.out.println("Accumulator, a = " + a + ", b = " + b);
return a + b.length();
,
(a, b) ->
System.out.println("Combiner");
return a * b;
);
System.out.println(ijk);
This is the output:
Accumulator, a = 0, b = ipsum
Accumulator, a = 0, b = lorem
Accumulator, a = 0, b = sit
Combiner
Accumulator, a = 0, b = amet
Combiner
Combiner
300
I see that the Accumulator and Combiner are accessed both, but only the multiplication is return. So what's happen with the sum?
java java-8 java-stream reduce
I try to understand how does the reduce()
method work in java-8.
For example I have this code:
public class App
public static void main(String[] args)
String[] arr = "lorem", "ipsum", "sit", "amet";
List<String> strs = Arrays.asList(arr);
int ijk = strs.stream().reduce(0,
(a, b) ->
System.out.println("Accumulator, a = " + a + ", b = " + b);
return a + b.length();
,
(a, b) ->
System.out.println("Combiner");
return a * b;
);
System.out.println(ijk);
And the output is this:
Accumulator, a = 0, b = lorem
Accumulator, a = 5, b = ipsum
Accumulator, a = 10, b = sit
Accumulator, a = 13, b = amet
17
It is a sum of the length of these strings. And I see that the combiner is not accessed, so it will not multiply the numbers, it only adds the numbers.
But if I have these stream:
int ijk = strs.parallelStream().reduce(0,
(a, b) ->
System.out.println("Accumulator, a = " + a + ", b = " + b);
return a + b.length();
,
(a, b) ->
System.out.println("Combiner");
return a * b;
);
System.out.println(ijk);
This is the output:
Accumulator, a = 0, b = ipsum
Accumulator, a = 0, b = lorem
Accumulator, a = 0, b = sit
Combiner
Accumulator, a = 0, b = amet
Combiner
Combiner
300
I see that the Accumulator and Combiner are accessed both, but only the multiplication is return. So what's happen with the sum?
java java-8 java-stream reduce
java java-8 java-stream reduce
edited May 3 at 13:30
Nikolas
14.9k53871
14.9k53871
asked May 3 at 12:47
gabygaby
701214
701214
6
combiner
is only called for a parallel stream. also read the documentation of reduce carefully, you can not do summation inaccumulator
and multiplication incombiner
and expect something meaningful as a result
– Eugene
May 3 at 12:50
add a comment |
6
combiner
is only called for a parallel stream. also read the documentation of reduce carefully, you can not do summation inaccumulator
and multiplication incombiner
and expect something meaningful as a result
– Eugene
May 3 at 12:50
6
6
combiner
is only called for a parallel stream. also read the documentation of reduce carefully, you can not do summation in accumulator
and multiplication in combiner
and expect something meaningful as a result– Eugene
May 3 at 12:50
combiner
is only called for a parallel stream. also read the documentation of reduce carefully, you can not do summation in accumulator
and multiplication in combiner
and expect something meaningful as a result– Eugene
May 3 at 12:50
add a comment |
4 Answers
4
active
oldest
votes
You should read the documentation of reduce
that says:
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)
In your case, you are breaking that law (doing a sum in accumulator
and multiplication in the combiner
), so the result you see for such an operation is really undefined and depends on how the Spliterator for the underlying source is implemented (don't do that!).
Besides, the combiner
is only called for a parallel stream.
Of course, your entire approach could be simplified to:
Arrays.asList("lorem", "ipsum", "sit", "amet")
.stream()
.mapToInt(String::length)
.sum();
If you are doing that just for learning purposes, a correct reduce
would be (to get the sum
):
strs.parallelStream()
.reduce(0,
(a, b) ->
System.out.println("Accumulator, a = " + a + ", b = " + b);
return a + b.length();
,
(a, b) ->
System.out.println("Combiner");
return a + b;
);
add a comment |
The Key Concepts: Identity, Accumulator, and Combiner
Stream.reduce() operation : let’s break down the operation’s participant elements into separate blocks. That way, we’ll understand more easily the role that each one plays
Identity – an element that is the initial value of the reduction operation and the default result if the stream is empty
itemAccumulator – a function that takes two parameters: a partial result of the reduction operation and the next element of the stream
Combiner – a function that takes two parameters: a partial result of the reduction operation and the next element of the stream
Combiner – a function used to combine the partial result of the reduction operation when the reduction is parallelized, or when there’s a mismatch between the types of the accumulator arguments and the types of the accumulator implementation
When a stream executes in parallel, the Java runtime splits the stream into multiple substreams. In such cases, we need to use a function to combine the results of the substreams into a single one. This is the role of the combiner
Case 1 : Combiner works with parallelStream
as showed in your example
Case 2 : Example accumulator with different type of arguments
In this case, we have a stream of User objects, and the types of the accumulator arguments are Integer and User. However, the accumulator implementation is a sum of Integers, so the compiler just can’t infer the type of the user parameter.
List<User> users = Arrays.asList(new User("John", 30), new User("Julie", 35));
int computedAges = users.stream().reduce(0, (partialAgeResult, user) -> partialAgeResult + user.getAge());
Compile Error
The method reduce(User, BinaryOperator<User>) in the type Stream<User> is not applicable for the arguments (int, (<no type> partialAgeResult, <no type> user) -> )
We can fix this issue by using a combiner: which is method reference Integer::sum
or by using lambda expression (a,b)->a+b
int computedAges = users.stream().reduce(0, (partialAgeResult, user) -> partialAgeResult + user.getAge(),Integer::sum);
To put it simply, if we use sequential streams and the types of the accumulator arguments and the types of its implementation match, we don’t need to use a combiner.
add a comment |
There are 3 ways to reduce using java-stream. In a nutshell, Stream::reduce
starts with two consequent items (or an identity value one with the first one) and performs an operation with them producing new reduced value. For each next item, the same happens and an operation is performed with the reduced value.
Let's say you have a stream of 'a'
, 'b'
, 'c'
and 'd'
. The reduction performs the following sequence of operations:
result = operationOn('a', 'b')
- theoperationOn
might be anything (sum of the lengths of inputs..)result = operationOn(result, 'c')
result = operationOn(result, 'd')
result is returned
The methods are:
Optional<T> reduce(BinaryOperator<T> accumulator)
performs a reduction on the elements. Starts with the first two items producing a reduced value, then each item with the reduced value. TheOptional<T>
is returned since it is not guaranteed the input stream is not empty.T reduce(T identity, BinaryOperator<T> accumulator)
does the same as the method above, except the identity value is given as the first item. TheT
is returned since there is always at least one item guaranteed, because ofT identity
.U reduce(U identity, BiFunction<U,? super T, U> accumulator, BinaryOperator<U> combiner)
does the same as the method above, with an addition that the functions are combined. TheU
is returned since there is always at least one item guaranteed, because ofU identity
.
Calling the identity value a “default value” is misleading, if not worse. If you want a default value, usereduce(function).orElse(default)
. Further, the explanation for the 3rd overload, “with an addition that the functions are combined” is not really helpful. The point of the third version is, that it allows to reduce to a different result type than the Stream’s element type. The difference to the straight-forward alternative,stream.map(…).reduce(id, function)
is that you can use an optimized function for combining a source element (T
) with a result element (U
), if there as one.
– Holger
May 6 at 10:46
"initial" might be better than "default" right? I still improve my English. Thanks for a note :)
– Nikolas
May 6 at 10:48
Well, the term “identity value” is already a term on its own, which has no better replacement. Neither “initial” nor “default” will be sufficient. The contract of an identity value is explained right in the documentation, but, as explained in this answer, it’s a term, not just invented for the reduction operation.
– Holger
May 6 at 10:51
add a comment |
I assume you chose to do addition and multiplication just as a demo to see what exactly happens.
As you already noticed, and as was already mentioned, the combiner is only called on parallel streams.
In short, on parallel strams, a part of the stream (resp. the underlying Spliterator) is cut off and processed by a different thread. After several parts are processed, their result is combined with a combiner.
In your case, the four elements all are processed by a different thread, and combination happens element-wise then. That's why you don't see any addition (apart from 0 +
) being applied, but only multiplication.
In order to get a meaningful result, however, you should switch from *
to +
and instead do a more meaningful output.
add a comment |
Your Answer
StackExchange.ifUsing("editor", function ()
StackExchange.using("externalEditor", function ()
StackExchange.using("snippets", function ()
StackExchange.snippets.init();
);
);
, "code-snippets");
StackExchange.ready(function()
var channelOptions =
tags: "".split(" "),
id: "1"
;
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function()
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled)
StackExchange.using("snippets", function()
createEditor();
);
else
createEditor();
);
function createEditor()
StackExchange.prepareEditor(
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader:
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
,
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
);
);
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f55970149%2fhow-does-the-reduce-method-work-in-java-8%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
4 Answers
4
active
oldest
votes
4 Answers
4
active
oldest
votes
active
oldest
votes
active
oldest
votes
You should read the documentation of reduce
that says:
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)
In your case, you are breaking that law (doing a sum in accumulator
and multiplication in the combiner
), so the result you see for such an operation is really undefined and depends on how the Spliterator for the underlying source is implemented (don't do that!).
Besides, the combiner
is only called for a parallel stream.
Of course, your entire approach could be simplified to:
Arrays.asList("lorem", "ipsum", "sit", "amet")
.stream()
.mapToInt(String::length)
.sum();
If you are doing that just for learning purposes, a correct reduce
would be (to get the sum
):
strs.parallelStream()
.reduce(0,
(a, b) ->
System.out.println("Accumulator, a = " + a + ", b = " + b);
return a + b.length();
,
(a, b) ->
System.out.println("Combiner");
return a + b;
);
add a comment |
You should read the documentation of reduce
that says:
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)
In your case, you are breaking that law (doing a sum in accumulator
and multiplication in the combiner
), so the result you see for such an operation is really undefined and depends on how the Spliterator for the underlying source is implemented (don't do that!).
Besides, the combiner
is only called for a parallel stream.
Of course, your entire approach could be simplified to:
Arrays.asList("lorem", "ipsum", "sit", "amet")
.stream()
.mapToInt(String::length)
.sum();
If you are doing that just for learning purposes, a correct reduce
would be (to get the sum
):
strs.parallelStream()
.reduce(0,
(a, b) ->
System.out.println("Accumulator, a = " + a + ", b = " + b);
return a + b.length();
,
(a, b) ->
System.out.println("Combiner");
return a + b;
);
add a comment |
You should read the documentation of reduce
that says:
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)
In your case, you are breaking that law (doing a sum in accumulator
and multiplication in the combiner
), so the result you see for such an operation is really undefined and depends on how the Spliterator for the underlying source is implemented (don't do that!).
Besides, the combiner
is only called for a parallel stream.
Of course, your entire approach could be simplified to:
Arrays.asList("lorem", "ipsum", "sit", "amet")
.stream()
.mapToInt(String::length)
.sum();
If you are doing that just for learning purposes, a correct reduce
would be (to get the sum
):
strs.parallelStream()
.reduce(0,
(a, b) ->
System.out.println("Accumulator, a = " + a + ", b = " + b);
return a + b.length();
,
(a, b) ->
System.out.println("Combiner");
return a + b;
);
You should read the documentation of reduce
that says:
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)
In your case, you are breaking that law (doing a sum in accumulator
and multiplication in the combiner
), so the result you see for such an operation is really undefined and depends on how the Spliterator for the underlying source is implemented (don't do that!).
Besides, the combiner
is only called for a parallel stream.
Of course, your entire approach could be simplified to:
Arrays.asList("lorem", "ipsum", "sit", "amet")
.stream()
.mapToInt(String::length)
.sum();
If you are doing that just for learning purposes, a correct reduce
would be (to get the sum
):
strs.parallelStream()
.reduce(0,
(a, b) ->
System.out.println("Accumulator, a = " + a + ", b = " + b);
return a + b.length();
,
(a, b) ->
System.out.println("Combiner");
return a + b;
);
edited May 3 at 13:12
John Kugelman
251k55411463
251k55411463
answered May 3 at 12:57
EugeneEugene
73.7k9104179
73.7k9104179
add a comment |
add a comment |
The Key Concepts: Identity, Accumulator, and Combiner
Stream.reduce() operation : let’s break down the operation’s participant elements into separate blocks. That way, we’ll understand more easily the role that each one plays
Identity – an element that is the initial value of the reduction operation and the default result if the stream is empty
itemAccumulator – a function that takes two parameters: a partial result of the reduction operation and the next element of the stream
Combiner – a function that takes two parameters: a partial result of the reduction operation and the next element of the stream
Combiner – a function used to combine the partial result of the reduction operation when the reduction is parallelized, or when there’s a mismatch between the types of the accumulator arguments and the types of the accumulator implementation
When a stream executes in parallel, the Java runtime splits the stream into multiple substreams. In such cases, we need to use a function to combine the results of the substreams into a single one. This is the role of the combiner
Case 1 : Combiner works with parallelStream
as showed in your example
Case 2 : Example accumulator with different type of arguments
In this case, we have a stream of User objects, and the types of the accumulator arguments are Integer and User. However, the accumulator implementation is a sum of Integers, so the compiler just can’t infer the type of the user parameter.
List<User> users = Arrays.asList(new User("John", 30), new User("Julie", 35));
int computedAges = users.stream().reduce(0, (partialAgeResult, user) -> partialAgeResult + user.getAge());
Compile Error
The method reduce(User, BinaryOperator<User>) in the type Stream<User> is not applicable for the arguments (int, (<no type> partialAgeResult, <no type> user) -> )
We can fix this issue by using a combiner: which is method reference Integer::sum
or by using lambda expression (a,b)->a+b
int computedAges = users.stream().reduce(0, (partialAgeResult, user) -> partialAgeResult + user.getAge(),Integer::sum);
To put it simply, if we use sequential streams and the types of the accumulator arguments and the types of its implementation match, we don’t need to use a combiner.
add a comment |
The Key Concepts: Identity, Accumulator, and Combiner
Stream.reduce() operation : let’s break down the operation’s participant elements into separate blocks. That way, we’ll understand more easily the role that each one plays
Identity – an element that is the initial value of the reduction operation and the default result if the stream is empty
itemAccumulator – a function that takes two parameters: a partial result of the reduction operation and the next element of the stream
Combiner – a function that takes two parameters: a partial result of the reduction operation and the next element of the stream
Combiner – a function used to combine the partial result of the reduction operation when the reduction is parallelized, or when there’s a mismatch between the types of the accumulator arguments and the types of the accumulator implementation
When a stream executes in parallel, the Java runtime splits the stream into multiple substreams. In such cases, we need to use a function to combine the results of the substreams into a single one. This is the role of the combiner
Case 1 : Combiner works with parallelStream
as showed in your example
Case 2 : Example accumulator with different type of arguments
In this case, we have a stream of User objects, and the types of the accumulator arguments are Integer and User. However, the accumulator implementation is a sum of Integers, so the compiler just can’t infer the type of the user parameter.
List<User> users = Arrays.asList(new User("John", 30), new User("Julie", 35));
int computedAges = users.stream().reduce(0, (partialAgeResult, user) -> partialAgeResult + user.getAge());
Compile Error
The method reduce(User, BinaryOperator<User>) in the type Stream<User> is not applicable for the arguments (int, (<no type> partialAgeResult, <no type> user) -> )
We can fix this issue by using a combiner: which is method reference Integer::sum
or by using lambda expression (a,b)->a+b
int computedAges = users.stream().reduce(0, (partialAgeResult, user) -> partialAgeResult + user.getAge(),Integer::sum);
To put it simply, if we use sequential streams and the types of the accumulator arguments and the types of its implementation match, we don’t need to use a combiner.
add a comment |
The Key Concepts: Identity, Accumulator, and Combiner
Stream.reduce() operation : let’s break down the operation’s participant elements into separate blocks. That way, we’ll understand more easily the role that each one plays
Identity – an element that is the initial value of the reduction operation and the default result if the stream is empty
itemAccumulator – a function that takes two parameters: a partial result of the reduction operation and the next element of the stream
Combiner – a function that takes two parameters: a partial result of the reduction operation and the next element of the stream
Combiner – a function used to combine the partial result of the reduction operation when the reduction is parallelized, or when there’s a mismatch between the types of the accumulator arguments and the types of the accumulator implementation
When a stream executes in parallel, the Java runtime splits the stream into multiple substreams. In such cases, we need to use a function to combine the results of the substreams into a single one. This is the role of the combiner
Case 1 : Combiner works with parallelStream
as showed in your example
Case 2 : Example accumulator with different type of arguments
In this case, we have a stream of User objects, and the types of the accumulator arguments are Integer and User. However, the accumulator implementation is a sum of Integers, so the compiler just can’t infer the type of the user parameter.
List<User> users = Arrays.asList(new User("John", 30), new User("Julie", 35));
int computedAges = users.stream().reduce(0, (partialAgeResult, user) -> partialAgeResult + user.getAge());
Compile Error
The method reduce(User, BinaryOperator<User>) in the type Stream<User> is not applicable for the arguments (int, (<no type> partialAgeResult, <no type> user) -> )
We can fix this issue by using a combiner: which is method reference Integer::sum
or by using lambda expression (a,b)->a+b
int computedAges = users.stream().reduce(0, (partialAgeResult, user) -> partialAgeResult + user.getAge(),Integer::sum);
To put it simply, if we use sequential streams and the types of the accumulator arguments and the types of its implementation match, we don’t need to use a combiner.
The Key Concepts: Identity, Accumulator, and Combiner
Stream.reduce() operation : let’s break down the operation’s participant elements into separate blocks. That way, we’ll understand more easily the role that each one plays
Identity – an element that is the initial value of the reduction operation and the default result if the stream is empty
itemAccumulator – a function that takes two parameters: a partial result of the reduction operation and the next element of the stream
Combiner – a function that takes two parameters: a partial result of the reduction operation and the next element of the stream
Combiner – a function used to combine the partial result of the reduction operation when the reduction is parallelized, or when there’s a mismatch between the types of the accumulator arguments and the types of the accumulator implementation
When a stream executes in parallel, the Java runtime splits the stream into multiple substreams. In such cases, we need to use a function to combine the results of the substreams into a single one. This is the role of the combiner
Case 1 : Combiner works with parallelStream
as showed in your example
Case 2 : Example accumulator with different type of arguments
In this case, we have a stream of User objects, and the types of the accumulator arguments are Integer and User. However, the accumulator implementation is a sum of Integers, so the compiler just can’t infer the type of the user parameter.
List<User> users = Arrays.asList(new User("John", 30), new User("Julie", 35));
int computedAges = users.stream().reduce(0, (partialAgeResult, user) -> partialAgeResult + user.getAge());
Compile Error
The method reduce(User, BinaryOperator<User>) in the type Stream<User> is not applicable for the arguments (int, (<no type> partialAgeResult, <no type> user) -> )
We can fix this issue by using a combiner: which is method reference Integer::sum
or by using lambda expression (a,b)->a+b
int computedAges = users.stream().reduce(0, (partialAgeResult, user) -> partialAgeResult + user.getAge(),Integer::sum);
To put it simply, if we use sequential streams and the types of the accumulator arguments and the types of its implementation match, we don’t need to use a combiner.
edited May 3 at 15:12
answered May 3 at 13:00
DeadpoolDeadpool
9,0022831
9,0022831
add a comment |
add a comment |
There are 3 ways to reduce using java-stream. In a nutshell, Stream::reduce
starts with two consequent items (or an identity value one with the first one) and performs an operation with them producing new reduced value. For each next item, the same happens and an operation is performed with the reduced value.
Let's say you have a stream of 'a'
, 'b'
, 'c'
and 'd'
. The reduction performs the following sequence of operations:
result = operationOn('a', 'b')
- theoperationOn
might be anything (sum of the lengths of inputs..)result = operationOn(result, 'c')
result = operationOn(result, 'd')
result is returned
The methods are:
Optional<T> reduce(BinaryOperator<T> accumulator)
performs a reduction on the elements. Starts with the first two items producing a reduced value, then each item with the reduced value. TheOptional<T>
is returned since it is not guaranteed the input stream is not empty.T reduce(T identity, BinaryOperator<T> accumulator)
does the same as the method above, except the identity value is given as the first item. TheT
is returned since there is always at least one item guaranteed, because ofT identity
.U reduce(U identity, BiFunction<U,? super T, U> accumulator, BinaryOperator<U> combiner)
does the same as the method above, with an addition that the functions are combined. TheU
is returned since there is always at least one item guaranteed, because ofU identity
.
Calling the identity value a “default value” is misleading, if not worse. If you want a default value, usereduce(function).orElse(default)
. Further, the explanation for the 3rd overload, “with an addition that the functions are combined” is not really helpful. The point of the third version is, that it allows to reduce to a different result type than the Stream’s element type. The difference to the straight-forward alternative,stream.map(…).reduce(id, function)
is that you can use an optimized function for combining a source element (T
) with a result element (U
), if there as one.
– Holger
May 6 at 10:46
"initial" might be better than "default" right? I still improve my English. Thanks for a note :)
– Nikolas
May 6 at 10:48
Well, the term “identity value” is already a term on its own, which has no better replacement. Neither “initial” nor “default” will be sufficient. The contract of an identity value is explained right in the documentation, but, as explained in this answer, it’s a term, not just invented for the reduction operation.
– Holger
May 6 at 10:51
add a comment |
There are 3 ways to reduce using java-stream. In a nutshell, Stream::reduce
starts with two consequent items (or an identity value one with the first one) and performs an operation with them producing new reduced value. For each next item, the same happens and an operation is performed with the reduced value.
Let's say you have a stream of 'a'
, 'b'
, 'c'
and 'd'
. The reduction performs the following sequence of operations:
result = operationOn('a', 'b')
- theoperationOn
might be anything (sum of the lengths of inputs..)result = operationOn(result, 'c')
result = operationOn(result, 'd')
result is returned
The methods are:
Optional<T> reduce(BinaryOperator<T> accumulator)
performs a reduction on the elements. Starts with the first two items producing a reduced value, then each item with the reduced value. TheOptional<T>
is returned since it is not guaranteed the input stream is not empty.T reduce(T identity, BinaryOperator<T> accumulator)
does the same as the method above, except the identity value is given as the first item. TheT
is returned since there is always at least one item guaranteed, because ofT identity
.U reduce(U identity, BiFunction<U,? super T, U> accumulator, BinaryOperator<U> combiner)
does the same as the method above, with an addition that the functions are combined. TheU
is returned since there is always at least one item guaranteed, because ofU identity
.
Calling the identity value a “default value” is misleading, if not worse. If you want a default value, usereduce(function).orElse(default)
. Further, the explanation for the 3rd overload, “with an addition that the functions are combined” is not really helpful. The point of the third version is, that it allows to reduce to a different result type than the Stream’s element type. The difference to the straight-forward alternative,stream.map(…).reduce(id, function)
is that you can use an optimized function for combining a source element (T
) with a result element (U
), if there as one.
– Holger
May 6 at 10:46
"initial" might be better than "default" right? I still improve my English. Thanks for a note :)
– Nikolas
May 6 at 10:48
Well, the term “identity value” is already a term on its own, which has no better replacement. Neither “initial” nor “default” will be sufficient. The contract of an identity value is explained right in the documentation, but, as explained in this answer, it’s a term, not just invented for the reduction operation.
– Holger
May 6 at 10:51
add a comment |
There are 3 ways to reduce using java-stream. In a nutshell, Stream::reduce
starts with two consequent items (or an identity value one with the first one) and performs an operation with them producing new reduced value. For each next item, the same happens and an operation is performed with the reduced value.
Let's say you have a stream of 'a'
, 'b'
, 'c'
and 'd'
. The reduction performs the following sequence of operations:
result = operationOn('a', 'b')
- theoperationOn
might be anything (sum of the lengths of inputs..)result = operationOn(result, 'c')
result = operationOn(result, 'd')
result is returned
The methods are:
Optional<T> reduce(BinaryOperator<T> accumulator)
performs a reduction on the elements. Starts with the first two items producing a reduced value, then each item with the reduced value. TheOptional<T>
is returned since it is not guaranteed the input stream is not empty.T reduce(T identity, BinaryOperator<T> accumulator)
does the same as the method above, except the identity value is given as the first item. TheT
is returned since there is always at least one item guaranteed, because ofT identity
.U reduce(U identity, BiFunction<U,? super T, U> accumulator, BinaryOperator<U> combiner)
does the same as the method above, with an addition that the functions are combined. TheU
is returned since there is always at least one item guaranteed, because ofU identity
.
There are 3 ways to reduce using java-stream. In a nutshell, Stream::reduce
starts with two consequent items (or an identity value one with the first one) and performs an operation with them producing new reduced value. For each next item, the same happens and an operation is performed with the reduced value.
Let's say you have a stream of 'a'
, 'b'
, 'c'
and 'd'
. The reduction performs the following sequence of operations:
result = operationOn('a', 'b')
- theoperationOn
might be anything (sum of the lengths of inputs..)result = operationOn(result, 'c')
result = operationOn(result, 'd')
result is returned
The methods are:
Optional<T> reduce(BinaryOperator<T> accumulator)
performs a reduction on the elements. Starts with the first two items producing a reduced value, then each item with the reduced value. TheOptional<T>
is returned since it is not guaranteed the input stream is not empty.T reduce(T identity, BinaryOperator<T> accumulator)
does the same as the method above, except the identity value is given as the first item. TheT
is returned since there is always at least one item guaranteed, because ofT identity
.U reduce(U identity, BiFunction<U,? super T, U> accumulator, BinaryOperator<U> combiner)
does the same as the method above, with an addition that the functions are combined. TheU
is returned since there is always at least one item guaranteed, because ofU identity
.
edited May 6 at 10:54
answered May 3 at 13:08
NikolasNikolas
14.9k53871
14.9k53871
Calling the identity value a “default value” is misleading, if not worse. If you want a default value, usereduce(function).orElse(default)
. Further, the explanation for the 3rd overload, “with an addition that the functions are combined” is not really helpful. The point of the third version is, that it allows to reduce to a different result type than the Stream’s element type. The difference to the straight-forward alternative,stream.map(…).reduce(id, function)
is that you can use an optimized function for combining a source element (T
) with a result element (U
), if there as one.
– Holger
May 6 at 10:46
"initial" might be better than "default" right? I still improve my English. Thanks for a note :)
– Nikolas
May 6 at 10:48
Well, the term “identity value” is already a term on its own, which has no better replacement. Neither “initial” nor “default” will be sufficient. The contract of an identity value is explained right in the documentation, but, as explained in this answer, it’s a term, not just invented for the reduction operation.
– Holger
May 6 at 10:51
add a comment |
Calling the identity value a “default value” is misleading, if not worse. If you want a default value, usereduce(function).orElse(default)
. Further, the explanation for the 3rd overload, “with an addition that the functions are combined” is not really helpful. The point of the third version is, that it allows to reduce to a different result type than the Stream’s element type. The difference to the straight-forward alternative,stream.map(…).reduce(id, function)
is that you can use an optimized function for combining a source element (T
) with a result element (U
), if there as one.
– Holger
May 6 at 10:46
"initial" might be better than "default" right? I still improve my English. Thanks for a note :)
– Nikolas
May 6 at 10:48
Well, the term “identity value” is already a term on its own, which has no better replacement. Neither “initial” nor “default” will be sufficient. The contract of an identity value is explained right in the documentation, but, as explained in this answer, it’s a term, not just invented for the reduction operation.
– Holger
May 6 at 10:51
Calling the identity value a “default value” is misleading, if not worse. If you want a default value, use
reduce(function).orElse(default)
. Further, the explanation for the 3rd overload, “with an addition that the functions are combined” is not really helpful. The point of the third version is, that it allows to reduce to a different result type than the Stream’s element type. The difference to the straight-forward alternative, stream.map(…).reduce(id, function)
is that you can use an optimized function for combining a source element (T
) with a result element (U
), if there as one.– Holger
May 6 at 10:46
Calling the identity value a “default value” is misleading, if not worse. If you want a default value, use
reduce(function).orElse(default)
. Further, the explanation for the 3rd overload, “with an addition that the functions are combined” is not really helpful. The point of the third version is, that it allows to reduce to a different result type than the Stream’s element type. The difference to the straight-forward alternative, stream.map(…).reduce(id, function)
is that you can use an optimized function for combining a source element (T
) with a result element (U
), if there as one.– Holger
May 6 at 10:46
"initial" might be better than "default" right? I still improve my English. Thanks for a note :)
– Nikolas
May 6 at 10:48
"initial" might be better than "default" right? I still improve my English. Thanks for a note :)
– Nikolas
May 6 at 10:48
Well, the term “identity value” is already a term on its own, which has no better replacement. Neither “initial” nor “default” will be sufficient. The contract of an identity value is explained right in the documentation, but, as explained in this answer, it’s a term, not just invented for the reduction operation.
– Holger
May 6 at 10:51
Well, the term “identity value” is already a term on its own, which has no better replacement. Neither “initial” nor “default” will be sufficient. The contract of an identity value is explained right in the documentation, but, as explained in this answer, it’s a term, not just invented for the reduction operation.
– Holger
May 6 at 10:51
add a comment |
I assume you chose to do addition and multiplication just as a demo to see what exactly happens.
As you already noticed, and as was already mentioned, the combiner is only called on parallel streams.
In short, on parallel strams, a part of the stream (resp. the underlying Spliterator) is cut off and processed by a different thread. After several parts are processed, their result is combined with a combiner.
In your case, the four elements all are processed by a different thread, and combination happens element-wise then. That's why you don't see any addition (apart from 0 +
) being applied, but only multiplication.
In order to get a meaningful result, however, you should switch from *
to +
and instead do a more meaningful output.
add a comment |
I assume you chose to do addition and multiplication just as a demo to see what exactly happens.
As you already noticed, and as was already mentioned, the combiner is only called on parallel streams.
In short, on parallel strams, a part of the stream (resp. the underlying Spliterator) is cut off and processed by a different thread. After several parts are processed, their result is combined with a combiner.
In your case, the four elements all are processed by a different thread, and combination happens element-wise then. That's why you don't see any addition (apart from 0 +
) being applied, but only multiplication.
In order to get a meaningful result, however, you should switch from *
to +
and instead do a more meaningful output.
add a comment |
I assume you chose to do addition and multiplication just as a demo to see what exactly happens.
As you already noticed, and as was already mentioned, the combiner is only called on parallel streams.
In short, on parallel strams, a part of the stream (resp. the underlying Spliterator) is cut off and processed by a different thread. After several parts are processed, their result is combined with a combiner.
In your case, the four elements all are processed by a different thread, and combination happens element-wise then. That's why you don't see any addition (apart from 0 +
) being applied, but only multiplication.
In order to get a meaningful result, however, you should switch from *
to +
and instead do a more meaningful output.
I assume you chose to do addition and multiplication just as a demo to see what exactly happens.
As you already noticed, and as was already mentioned, the combiner is only called on parallel streams.
In short, on parallel strams, a part of the stream (resp. the underlying Spliterator) is cut off and processed by a different thread. After several parts are processed, their result is combined with a combiner.
In your case, the four elements all are processed by a different thread, and combination happens element-wise then. That's why you don't see any addition (apart from 0 +
) being applied, but only multiplication.
In order to get a meaningful result, however, you should switch from *
to +
and instead do a more meaningful output.
answered May 3 at 12:56
glglglglglgl
68.7k796169
68.7k796169
add a comment |
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f55970149%2fhow-does-the-reduce-method-work-in-java-8%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
6
combiner
is only called for a parallel stream. also read the documentation of reduce carefully, you can not do summation inaccumulator
and multiplication incombiner
and expect something meaningful as a result– Eugene
May 3 at 12:50