Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How does the reduce() method work in Java 8?

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 replace stream with parallelStream:

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?


1 Answers

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;
            });
like image 175
Eugene Avatar answered Sep 09 '25 15:09

Eugene