当前位置: 首页 > 知识库问答 >
问题:

Java8流产生的代码比普通命令式循环慢吗?

莫欣悦
2023-03-14

关于函数式编程的宣传太多了,特别是新的Java8Streams API。它被标榜为旧的好循环和命令式范例的很好的替代。的确,有时它看起来很漂亮,而且做得很好。但是性能呢?

例如。这是一篇关于它的好文章:Java8:No more loops使用循环,只需一次迭代就可以完成所有的工作。但是使用一个新的流API,您将会使用多个循环,这会使它慢得多(对吗?)。看看他们的第一个样本。循环在大多数情况下甚至不会遍历整个数组。然而,要使用新的流API进行过滤,您必须循环遍历整个数组以筛选出所有候选项,然后才能获得第一个。

在这篇文章中提到了一些懒惰:

我们首先使用filter操作查找所有具有Java标记的文章,然后使用findFirst()操作获取第一个出现的内容。由于流是惰性的,并且filter返回一个流,这种方法只处理元素,直到找到第一个匹配。

我做了一个简单的测试,它表明旧的好的循环解决方案的速度是流方法的10倍。

public void test() {
    List<String> list = Arrays.asList(
            "First string",
            "Second string",
            "Third string",
            "Good string",
            "Another",
            "Best",
            "Super string",
            "Light",
            "Better",
            "For string",
            "Not string",
            "Great",
            "Super change",
            "Very nice",
            "Super cool",
            "Nice",
            "Very good",
            "Not yet string",
            "Let's do the string",
            "First string",
            "Low string",
            "Big bunny",
            "Superstar",
            "Last");

    long start = System.currentTimeMillis();
    for (int i = 0; i < 100000000; i++) {
        getFirstByLoop(list);
    }
    long end = System.currentTimeMillis();

    System.out.println("Loop: " + (end - start));

    start = System.currentTimeMillis();
    for (int i = 0; i < 100000000; i++) {
        getFirstByStream(list);
    }
    end = System.currentTimeMillis();

    System.out.println("Stream: " + (end - start));
}

public String getFirstByLoop(List<String> list) {

    for (String s : list) {
        if (s.endsWith("string")) {
            return s;
        }
    }

    return null;
}

public Optional<String> getFirstByStream(List<String> list) {
    return list.stream().filter(s -> s.endsWith("string")).findFirst();
}

结果是:

循环:517

溪流:5790

共有1个答案

濮俭
2023-03-14

问题:如果我要寻找最佳的代码性能,我应该使用旧的循环命令方法吗?

现在,可能是的。各种基准测试似乎表明,对于大多数测试来说,流比循环要慢。但速度并不是灾难性的慢。

反例:

惰性流可以为某些问题提供性能上的好处;参见http://java.amitph.com/2014/01/java-8-streams-api-laziness.html

可以用循环做等价的事情,你不能只用循环做。

但底线是性能是复杂的,流还不是加快代码速度的灵丹妙药。

FP范式仅仅是为了让代码“更简洁、更易读”而不是关于性能吗?

但是,通过使用FP范例来表达,您也是在以一种潜在的方式来表达它,这种方式可以通过使用循环和赋值表达的代码来实现,而这种方式很难实现。FP代码也更适合于形式化方法;即正确性的正式证明。

(在讨论流的上下文中,“可以优化”的意思是在将来的Java版本中。)

 类似资料:
  • 在大多数情况下,Java8流允许的代码比老式的循环更易读。然而,根据我自己的经验和阅读的内容,使用stream而不是for循环可能会导致性能下降(或者偶尔会带来改进),这有时很难预测。 在一个大型项目中,为每个循环编写基准测试似乎是不可行的,因此,当决定是否用流替换循环时,有哪些关键因素(例如,集合的预期大小、通过筛选移除的值的预期百分比、迭代操作的复杂度、缩减或聚合的类型,等等)可能给出将导致的

  • 问题内容: 我正在使用django应用程序,该应用程序执行一些“ startswith” ORM操作,将列与unicode字符串进行比较。这将导致使用unicode字符串进行比较操作。LIKE BINARY可能会比普通LIKE慢吗? 我知道一般的答案是基准测试,但是我想对数据库有一个总体的了解,而不仅仅是我的应用程序,因为我以前从未见过LIKE BINARY查询。 我碰巧正在使用MySQL,但我总

  • 为什么我的SIMD向量4长度函数比原始向量长度方法慢3倍? SIMD矢量4长度函数: 幼稚的实现: 我用GCC(Ubuntu7.4.0-1Ubuntu1~18.04.1)7.4.0: SSE版本输出: 纯C版本输出:

  • 我试图将我的编程风格从命令式切换到声明式,但有一些概念困扰着我,比如当涉及到循环时的性能。例如,我有一个原始数据,在操作它之后,我希望得到3个预期结果:itemsHash、namesHash、rangeItemsHash 为了达到预期的结果,命令式只循环一次,而声明式至少循环3次(、、)。哪一个比较好?在性能上有什么权衡吗?

  • 考虑到我有2个CPU核心的事实,并行版本不是应该更快吗?有人能给我一个提示为什么并行版本比较慢吗?

  • 我已经创建了一个使用流执行矩阵乘法的模块。可以在这里找到:https://github.com/firefly-math/firefly-math-lineal-real/ 当我在大小为100x100和1000x1000的矩阵上运行基准测试时,发现Apache Commons Math(使用for循环)比相应的流实现快10倍(大致)。 我在基准测试中做错了什么吗(希望是:))? 我添加了测试的方法