在mapToInt之后调用map是否有任何优势,如果需要的话

Joh*_*iva 10 java performance java-8 java-stream

我试图计算列表中值的平方和.以下是三种变体,它们都计算出所需的值.我想知道哪一个最有效率.我期待第三个更高效,因为自动拳击只进行一次.

    // sum of squares
    int sum = list.stream().map(x -> x * x).reduce((x, y) -> x + y).get();
    System.out.println("sum of squares: " + sum);

    sum = list.stream().mapToInt(x -> x * x).sum();
    System.out.println("sum of squares: " + sum);

    sum = list.stream().mapToInt(x -> x).map(x -> x * x).sum();
    System.out.println("sum of squares: " + sum);
Run Code Online (Sandbox Code Playgroud)

ass*_*ias 10

如有疑问,请测试!使用jmh,我在100k元素列表中得到以下结果(以微秒为单位,越小越好):

Benchmark                        Mode  Samples     Score    Error  Units
c.a.p.SO32462798.for_loop        avgt       10   119.110    0.921  us/op
c.a.p.SO32462798.mapToInt        avgt       10   129.702    1.040  us/op
c.a.p.SO32462798.mapToInt_map    avgt       10   129.753    1.516  us/op
c.a.p.SO32462798.map_reduce      avgt       10  1262.802   12.197  us/op
c.a.p.SO32462798.summingInt      avgt       10   134.821    1.203  us/op
Run Code Online (Sandbox Code Playgroud)

所以你有,从快到慢:

  • for(int i : list) sum += i*i;
  • mapToInt(x -> x * x).sum()mapToInt(x -> x).map(x -> x * x).sum()
  • collect(Collectors.summingInt(x -> x * x))
  • map(x -> x * x).reduce((x, y) -> x + y).get()

请注意,结果很大程度上取决于JIT优化.如果映射中的逻辑更复杂,则某些优化可能不可用(更长的代码=更少的内联),在这种情况下,流版本可能需要比for循环多4-5倍的时间 - 但如果该逻辑是CPU重的话差异将再次减少.分析您的实际应用程序将为您提供更多信息.


基准代码供参考:

@State(Scope.Benchmark)
@BenchmarkMode(Mode.AverageTime)
public class SO32462798 {

  List<Integer> list;

  @Setup public void setup() {
    list = new Random().ints(100_000).boxed().collect(toList());
  }

  @Benchmark public int for_loop() {
    int sum = 0;
    for (int i : list) sum += i * i;
    return sum;
  }

  @Benchmark public int summingInt() {
    return list.stream().collect(Collectors.summingInt(x -> x * x));
  }

  @Benchmark public int mapToInt() {
    return list.stream().mapToInt(x -> x * x).sum();
  }

  @Benchmark public int mapToInt_map() {
    return list.stream().mapToInt(x -> x).map(x -> x * x).sum();
  }

  @Benchmark public int map_reduce() {
    return list.stream().map(x -> x * x).reduce((x, y) -> x + y).get();
  }
}
Run Code Online (Sandbox Code Playgroud)

  • 添加一个普通的旧循环实现作为参考会很好。像`int sum = 0;for(int i : list) sum+=i*i; 返还金额;`. 另一种选择是使用`list.stream().collect(Collectors.summingInt(x -&gt; x*x));` (2认同)
  • 顺便说一句,在`setup()` 中使用`list = new Random().ints(100_000).boxed().collect(Collectors.toList());` 会很好! (2认同)