Java 8 - reduce(0, Integer::sum) 和 reduce(0, (a, b) -> a+b) 的区别是什么?

4

我刚接触 Java 8,并找到了一些实现加法乘法减法的方法。我想问如何只进行加法

我写了下面这段代码,并将输出结果分别存储在 Sum1 和 Sum2 中。两种方法 reduce(0, Integer::sum).reduce(0, (a, b) -> a+b); 得到的结果相同。从性能角度来看,使用哪种方法最好?如果使用大整数值,应该选择哪种方法?为什么?

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);

7
我认为最好的方法是使用numbers.stream().mapToInt(Integer::intValue).sum();,因为reduce存在多个自动装箱。 - Hadi J
哦,好的。我错过了这种方法的想法 :) 有使用它的强烈理由吗?如果您可以进一步指导我?我更新了我的陈述。 - Jeff Cook
请查看此链接:https://dev59.com/Fl0a5IYBdhLWcg3wmZ3C - Hadi J
2
最快的方法是使用普通循环,而不是流。 - Eugene
1
reduce(0, Integer::sum)reduce((a, b) -> a + b)之间没有区别。对于第一个,每次评估都会调用Integer.sum(…)方法,对于第二个,将调用在您的类中创建的方法,该方法完全相同,即return a+b;。这些考虑与Function.identity()或t->t类似。虽然我们这里没有共享生成的实例(但是),调用现有方法可以减少代码并提高最终优化共享代码的可能性,但可能会妨碍调试。 - Holger
显示剩余3条评论
2个回答

5
什么是针对性能使用大整数值的最佳方法?为什么?
主要思想是在使用原始数据和它们的对象表示时忽略自动装箱/拆箱。将两个“Integer”对象相加比将两个“int”原语相加复杂得多。您可以在this post中找到更多细节。
因此,最好的情况是当您有一个原始数组“int []”,并将其元素相加到类型为“int”的累加器中。因此,这里没有涉及任何装箱操作。
较差(但不是最差)的情况是将对象“Integer”与原始“int”相加。它会导致第一个参数的拆箱。当您的初始数组或集合包含对象(例如Integer [],List等),而累加器是原始的时,就会发生这种情况。这将导致每个集合元素的拆箱恰好一次,而累加器将保持不变。
最坏的情况是将对象的集合总和到“Integer”累加器中。
  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();
      

很好的总结!如果您能为不同的替代方案提供一些jmh基准测试,那将更好。 - Sean Patrick Floyd

0

有趣的事情: 在Intellij IDEA中,当您将鼠标悬停在sum()函数上时,会出现如下的工具提示:

enter image description here

返回此流中元素的总和。 这是归约的特例,等价于:
Public abstract int sum() 
//equal function to
return reduce(0, Integer::sum);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接