performance - Java 8流产生的代码是否比普通命令式循环慢?

标签 performance java-8 functional-programming java-stream

关于函数式编程,尤其是新的Java 8流API的宣传太多了。广告宣传它是旧的良好循环和命令式范例的良好替代品。
确实,有时它看起来不错,并且做得很好。但是性能如何呢?

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

在本文中提到了一些懒惰:

We first use the filter operation to find all articles that have the Java tag, then used the findFirst() operation to get the first occurrence. Since streams are lazy and filter returns a stream, this approach only processes elements until it finds the first match.



作者对此懒惰意味着什么?

我做了简单的测试,结果表明旧的好的循环解决方案比流方法快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

顺便说一句,如果我将使用String []而不是List的话,差异会更大!差不多100倍!

问题:如果我要寻找最佳的代码性能,是否应该使用旧的循环命令式方法? FP范式仅仅是为了使代码“更简明易读”,而不是关于性能吗?

或者

我错过了什么吗,新的流API至少可以和循环命令方法一样高效?

最佳答案

QUESTION: Should I use old loop imperative approach if I'm looking for the best code performance?


现在,可能是。各种基准似乎表明,在大多数测试中,流的速度比循环慢。虽然不是灾难性地慢。
反例:
  • 在某些情况下,并行流可以提供有用的加速。
  • 惰性流可以为某些问题提供性能上的好处;见http://java.amitph.com/2014/01/java-8-streams-api-laziness.html

  • 可以用循环来做等效的事情,而不能只用循环来做。
    但最重要的是,性能很复杂,流(尚未)不是加速代码的 Elixir 。

    Is FP paradigm is just to make code "more concise and readable" but not about performance?


    不完全是。 FP范例确实更加简洁,并且(对于熟悉它的人而言)更具可读性,这的确是正确的。
    但是,通过使用FP范例来表达,您还以一种可能被优化的方式来表达它,而使用循环和赋值表达的代码则很难实现这一方式。 FP代码也更适合形式方法。即正式的正确性证明。
    (在对流的讨论中,“可以优化”是指将来的​​某些Java版本中。)

    关于performance - Java 8流产生的代码是否比普通命令式循环慢?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48248883/

    相关文章:

    java - 在java中求一个数字的累加和时,我们应该使用字符串还是列表来存储数字?

    Java 应用程序性能问题

    java - MultiValueMap Lambda 问题

    function - Haskell 模式匹配在负数上失败

    python - 将日期时间字符串快速转换为秒 (Python3)

    Java比C快?

    java - 如何将类传递给方法并调用该类的静态方法

    java - 将 for-each 循环替换为 lambda 表达式

    algorithm - 如何用函数式语言编写简单的树算法?

    Emacs lisp 高阶函数支持