【问题标题】:Java Stream works slower than for loopJava Stream 的工作速度比 for 循环慢
【发布时间】:2020-04-19 14:34:31
【问题描述】:

有人告诉我,Java Stream 是处理大量数据的好选择,我最近做了一个比较测试。然而测试结果出乎意料:

问题来自 CodeWar:

假设有一个地铁最初有 100 人,每一站都有几个人上地铁,几个人下车。目标是统计经过大量停靠后仍留在地铁中的人数(100000)。

这是我的代码:

import java.util.ArrayList;

public class Metro1 {
    private final static int STOPS = 100000;
    private static ArrayList<int[]> metro = new ArrayList<int[]>();

    public static int sum1() {
        int sum = 0;
        for(int[] x: metro) {
            sum +=x[0] - x[1];
        }
        return sum;
    }

    public static int sum2() {
        return metro.stream()
                .mapToInt(x -> x[0]-x[1])
                .sum();
    }
    public static void main(String[] args) {
        long start=0;
        long end = 0;
        metro.add(new int[] {100,0});
        for(int i=1;i<STOPS;i++) {
            int in = (int) Math.round(Math.random() * 10);
            int out = (int) Math.round(Math.random() * 10);
            metro.add(new int[] {in,out});
        }
        System.out.println("Stops: " + metro.size());

        start = System.currentTimeMillis();
        System.out.println("sum1: " + sum1());
        end = System.currentTimeMillis();
        System.out.println("sum1 (for loop): " + String.valueOf(end-start) + " milliseconds.");


        start = System.currentTimeMillis();
        System.out.println("sum2: " + sum2());
        end = System.currentTimeMillis();
        System.out.println("sum1 (stream): " + String.valueOf(end-start) + " milliseconds.");

    }

}

我在 Eclipse 中运行代码,发现 sum1 比 sum2 快得多:

Stops: 100000
sum1: 79
sum1 (for loop): 6 milliseconds.
sum2: 79
sum1 (stream): 68 milliseconds.

我以为代码很简单,但是为什么流比for循环慢?

谢谢,

亚历克斯

【问题讨论】:

标签: java algorithm java-stream


【解决方案1】:

就像regexes 一样,特定的本地解析器可以更快,流可以提供一种快速简洁的数据处理方式。流的优点之一是在处理元素时可以最小化中间数据结构。另一个是cmets中已经提到的parallel切面。

但至于你的例子。

  • 依靠使用内部时钟的简单性能测试(即使我也这样做)并不是准确评估性能的最佳方式。使用 Java Microbench Harness 之类的东西进行测试。
  • 至于您的结果,请尝试以下方法:

    • STOPS 更改为100_000_000
    • 将您的信息流修改为
      return metro.stream().parallel() .mapToInt(x -> x[0]-x[1]) .sum();

这是我Windows, quad core i7 laptop 上的结果

Stops: 100000000
sum1: -7073
sum1 (for loop): 908 milliseconds.
sum2: -7073
sum1 (stream): 518 milliseconds.

【讨论】:

  • 谢谢。但是,即使我使用了 parallel(),我也无法使流更快地工作,实际上我在发布这个问题之前确实尝试过并行。我的笔记本电脑是双核 mac。
  • 我仔细检查了我的代码,发现其中有一个错误。现在带有 parallel() 的流工作得更快,几乎与 for 循环相同。再次感谢您的详细信息!
【解决方案2】:

流式 API 更多的是为了代码的可读性和可维护性。 For 循环可能会提供更好的性能,但有时性能可能不是衡量的重要指标。拥有可维护的代码同样重要。

我们可以使用具有非常大数据集的并行流,但同样不能保证更好的性能,因为这涉及其他开销并取决于可用资源。

【讨论】:

    猜你喜欢
    • 2012-03-03
    • 2016-04-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-08-02
    • 1970-01-01
    • 2017-10-13
    • 2021-07-28
    相关资源
    最近更新 更多