In Stream reduce method, must the identity always be 0 for sum and 1 for multiplication?

前端 未结 7 837
执念已碎
执念已碎 2020-11-29 06:05

I proceed with java 8 learning.

I have found an interesting behavior:

let\'s see code sample:

// identity value and accumulator and combiner         


        
7条回答
  •  野趣味
    野趣味 (楼主)
    2020-11-29 06:48

    The @holger answer greatly explain what is the identity for different function but doesn't explain why we need identity and why you have different results between parallel and sequential streams.

    Your problem can be reduced to summing a list of element knowing how to sum 2 elements.

    So let's take a list L = {12,32,10,18} and a summing function (a,b) -> a + b

    Like you learn at school you will do:

    (12,32) -> 12 + 32 -> 44
    (44,10) -> 44 + 10 -> 54
    (54,18) -> 54 + 18 -> 72
    

    Now imagine our list become L = {12}, how to sum this list? Here the identity (x op identity = x) comes.

    (0,12) -> 12
    

    So now you can understand why you get +1 to your sum if you put 1 instead of 0, that's because you initialize with a wrong value.

    (1,12) -> 1 + 12 -> 13
    (13,32) -> 13 + 32 -> 45
    (45,10) -> 45 + 10 -> 55
    (55,18) -> 55 + 18 -> 73
    

    So now, how can we improve speed? Parallelize things

    What if we can split our list and give those splitted list to 4 different thread (assuming 4-core cpu) and then combined it? This will give us L1 = {12}, L2 = {32}, L3 = {10}, L4 = {18}

    So with identity = 1

    • thread1: (1,12) -> 1+12 -> 13
    • thread2: (1,32) -> 1+32 -> 33
    • thread3: (1,10) -> 1+10 -> 11
    • thread4: (1,18) -> 1+18 -> 19

    and then combine, 13 + 33 + 11 +19, which is equal to 76, this explain why the error is propagated 4 times.

    In this case parallel can be less efficient.

    But this result depends on your machine and input list. Java won't create 1000 threads for 1000 elements and the error will propagate more slowly as the input grows.

    Try running this code summing one thousand 1s, the result is quite close to 1000

    public class StreamReduce {
    
    public static void main(String[] args) {
            int sum = IntStream.range(0, 1000).map(i -> 1).parallel().reduce(1, (r, e) -> r + e);
            System.out.println("sum: " + sum);
        }
    }
    

    Now you should understand why you have different results between parallel and sequential if you break the identity contract.

    See Oracle doc for proper way to write your sum


    What's the identity of a problem?

提交回复
热议问题