Search code examples
javacollectionsjava-8java-streamreduce

Java 8 - Difference between reduce(0, Integer::sum) and reduce(0, (a, b) -> a+b)


I am new to Java 8 and did find some ways to do addition, multiply and subtraction. I will be posting question for add only.

I have written below code and gathering output in Sum1 and Sum2. Both the methods reduce(0, Integer::sum) and .reduce(0, (a, b) -> a+b); gives the same result. What would be the best method to use performance wise and if using large integer values and why ?

List<Integer> numbers = Arrays.asList(1, 2, 1, 3, 3, 2, 4);

Integer sum1 = numbers.stream().reduce(0, (a, b) -> a+b);
System.out.println("SUM ="+sum1);

Integer product = numbers.stream().reduce(0, (a, b) -> a*b);
System.out.println("PRODUCT = "+product);

int sum2 = numbers.stream().reduce(0, Integer::sum);
System.out.println("SUM 2= "+sum2);

Optional<Integer> sum3 = numbers.stream().reduce((a, b) -> (a + b));
System.out.println("SUM3="+sum3);

// Updated as per  @Hadi J comment
int sum4 = numbers.stream().mapToInt(Integer::intValue).sum();
System.out.println("Sum ="+sum4);

Solution

  • What would be the best method to use performance wise and if using large integer values and why ?

    The main idea is to ignore auto-boxing/unboxing when juggling with primitives and their object representations.

    Adding two Integer objects is much more sophisticated than adding two int primitives. You can find more details in this post.

    So the best case is when you have a primitive array int[] and sum its elements into an accumulator of type int. Thus no boxing involved here.

    A worse (but not the worst) case is when you sum an object Integer with a primitive int. It will cause unboxing of the first argument. This is how it works when your initial array or collection contains objects (e.g. Integer[], List, etc...) and the accumulator is primitive. This will cause unboxing of each element of your collection exactly once, while the accumulator will remain the same.

    The worst case is summing up a collection of objects into an Integer accumulator.

    1. Plain old Java:

      • Summing Integer[] into Integer

        Integer[] array = {0,1,2,3,4,5,6,7,8,9};
        Integer sum = 0;
        // Each element of the array will be unboxed exactly once
        // sum will be unboxed 10 times
        // result of the sum will be boxed 10 times
        for(int i : array) sum += i;
        
      • Summing Integer[] into int

        Integer[] array = {0,1,2,3,4,5,6,7,8,9};
        int sum = 0;
        // Each element of the array will be unboxed exactly once
        // result of the sum will be boxed 10 times
        for(int i : array) sum += i;
        
      • Summing int[] into int

        int[] array = {0,1,2,3,4,5,6,7,8,9};
        int sum = 0;
        // No boxing/unboxing at all
        for(int i : array) sum += i;
        
    2. Stream API

      • Reducing sum of Stream<Integer>

        Stream<Integer> stream = Stream.of(0,1,2,3,4,5,6,7,8,9);
        // Each element will be unboxed exactly once
        // The accumulator will be unboxed 10 times
        // The result of the sum operation will be boxed 10 times
        Integer sum = stream.reduce(0, Integer::sum);
        
      • Reducing sum of Stream<Integer> mapping to primitive int

        Stream<Integer> stream = Stream.of(0,1,2,3,4,5,6,7,8,9);
        // Each element will be unboxed exactly once
        // Neither accumulator not result will be unboxed/boxed
        int sum = stream.mapToInt(Integer::intValue).reduce(0, Integer::sum);
        
      • Reducing sum of IntStream

        IntStream stream = IntStream.of(0,1,2,3,4,5,6,7,8,9);
        // No boxing/unboxing at all
        int sum = stream.reduce(0, Integer::sum);
        // same as
        int sum2 = stream.sum();