什么情况下不应该使用Java 8流?

6

我只是尝试了一些代码片段,发现使用简单的for循环比使用Java 8流获得了更好的性能结果。现在,我可能在理解这些事情方面有所遗漏。我需要帮助理解差异。以下是我的代码。

//Following takes almost 3ms

public int[] testPerf(int[] nums, int[] index) {
    List<Integer> arrayL = new ArrayList<>();
    for(int i =0; i< index.length; i++){
        arrayL.add(index[i], nums[i]);
    }
    return arrayL.stream().mapToInt(i -> i).toArray();
}

//Following takes almost 1ms

public int[] testPerf(int[] nums, int[] index) {
    List<Integer> arrayL = new ArrayList<>();
    for(int i =0; i< index.length; i++){
        arrayL.add(index[i], nums[i]);
    }
    int [] result = new int[index.length];
    for(int i =0; i< index.length; i++){
        result[i] = arrayL.get(i);
    }
    return result;
}

编辑:开始

我试图测试什么?将nums数组的元素插入到由索引数组指定的位置,形成最终结果。

Input: nums = [0,1,2,3,4], index = [0,1,2,2,1]
Output: [0,4,1,3,2]
Explanation:
nums       index     target
0            0        [0]
1            1        [0,1]
2            2        [0,1,2]
3            2        [0,1,3,2]
4            1        [0,4,1,3,2]

编辑:结束

请注意,我已经使用不同的输入和包含2k+元素的数组进行了测试,但是使用 for 循环的代码仍然表现更好。

请指导其他代码为什么需要更长时间?另外,请指出任何可以学习在哪些情况下不要使用 Java 流的参考资料?(在什么情况下不要过于复杂化?:))


第一种方法应该是一行 return Arrays.stream(nums).toArray(); - 你的代码不够优化。其次,微基准测试非常难以正确执行(而且你没有包含测试工具,这导致你得出了哪个更快的结论)。 - Elliott Frisch
7
流并不能保证更好的性能,但可以保证更好的代码可读性。你可以查看mapToInt的实现方式。 - dawis11
2
“使用简单的for循环比使用Java 8流给我带来了更好的性能结果”,这几乎总是正确的,因为使用流会有相当大的开销。只有在循环中的工作量较大且启用并行处理时,流才有可能优于旧式循环。 - Andreas
4
真的吗?为什么不使用return nums.clone();呢?因为这将是相同结果的更快的版本。并且这还完全忽略了 index 参数的存在,这个参数很可能有它的用途。 - Andreas
@ElliottFrisch,我添加了我要测试什么,这样你就可以将我在代码中做什么与为什么这样做联系起来。 - dev2d
显示剩余3条评论
2个回答

2
我喜欢使用流而非循环(实际上我喜欢任何东西都比循环好),因为流通常更容易编写且不易出错,即使没有测试,也因其声明式统一的语法。同样的原因,即使没有详细注释,非作者也通常更容易阅读流。此外,流是惰性的(考虑“拉取”),而循环是急切的(考虑“推动”)。而惰性通常更好。

不幸的是,流有时会有(相当大的)开销,因此在编写性能关键代码时需要注意,特别是在使用收集器时。

所以,像“尽可能使用流,必要时使用循环”。


1
流的目标是使代码更紧凑,更易读,减少样板代码,从而简化开发人员的工作。流的目标不是提高性能。在特定情况下,性能可能会与 for 循环有所不同。此外,流可能需要更多的内存。但是,流转换为字节码的结果可能取决于 JVM:Oracle JDK、Open JDK、IBM JDK 等。
如果您对内存和性能有严格要求,那么就没有现成的答案。您应该比较两种变体中的生产代码(而不是您在此处显示的示例)-使用 for 和使用流,然后选择符合您严格要求的内容。
但是...
但是,在大多数应用程序中,无论是 for 还是流,循环对性能的影响都非常低,与访问数据库或通过网络调用某些 Web 服务等其他操作相比。即使 for 比流快 x10,但数据库访问需要 2,000 毫秒,您也看不到真正的差异。
代码风格的差异可能非常重要。开发人员可以编写更易读的代码。当您团队中的一些开发人员去其他项目,而另一些新的开发人员加入您的团队并需要扩展现有代码时,他们将需要更少的时间来理解这样的代码。这并不是说理解 for 循环很难 :) 但是相对于 for 循环的代码,带有多个流的代码对许多开发人员来说更自然。
有一些特殊情况,其中流难以应用,例如同时循环2或3个集合。在这种情况下,流没有太多意义,for 循环更可取。
但总的来说,并没有规定何时使用或不使用特定结构。您知道任何 while 和 do-while 循环都可以用 for 循环替换。但为什么 Java 有不同类型的循环?因为其中一些可以在特定情况下更好地表达逻辑。流也是如此。如果您和您的团队对流不感到舒适,您不必使用它们。试试它们,使用它们超过3个月,然后在团队内讨论并决定每个开发人员是否应尽可能使用它们或应尝试避免使用它们。

你应该将流的性能与裸的for循环进行比较,因为这就是它们要抽象化的内容,而不是网络交互甚至操作系统交互。如果你这样做,你会发现流,正如我在我的答案中所写的那样,具有开销,这可能根据代码执行的内容是否关键。 - bobah
如果扩展和修改更高效但可读性较差的代码每月需要额外花费20,000美元,而性能较差但易于阅读和维护的代码每月需要额外花费1,000美元的CPU来提供相同的响应时间,那么客户更喜欢选择更便宜的解决方案,即提供相同结果的性能较差但易于维护的代码。只需计算您特定情况下两种方法的成本即可。 - mentallurg
性能始终是至关重要的。因为您总是需要决定是否付费。为云部署架构分布式系统是昂贵的,运行此类系统的费用是投资者不愿意承担的运营成本,由猴子编写的代码将很容易比由真正知道自己在做什么的人编写的代码多花费100倍。 - bobah
但是谢谢你,因为那些认为性能不再重要的人,我们将始终有客户来购买我们的云基础设施!:-D - bobah
许多开发人员在不问任何问题的情况下遵循教条。架构师考虑上下文:在一个上下文中正确的事情可能在另一个上下文中是错误的。 - mentallurg
显示剩余4条评论

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