【发布时间】:2018-01-14 11:00:01
【问题描述】:
关于函数式编程的炒作太多了,尤其是新的 Java 8 流 API。它被宣传为旧的良好循环和命令式范例的良好替代品。 确实,有时它看起来不错并且可以很好地完成工作。但是性能呢?
例如这是一篇很好的文章:Java 8: No more loops 使用循环,您可以通过一次迭代完成所有工作。但是使用新的流 API,您将链接多个循环,这会使其速度变慢(对吗?)。 看看他们的第一个样本。在大多数情况下,循环甚至不会遍历整个数组。但是,要使用新的流 API 进行过滤,您必须循环遍历整个数组以过滤掉所有候选者,然后您将能够获得第一个。
在这篇文章中提到了一些懒惰:
我们首先使用过滤器操作查找所有具有Java标签的文章,然后使用findFirst()操作获取第一次出现。由于流是惰性的并且过滤器返回一个流,因此这种方法只处理元素,直到找到第一个匹配项。
作者的懒惰是什么意思?
我做了简单的测试,它表明旧的良好循环解决方案的工作速度比流式方法快 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 至少可以与循环命令式方法一样高效?
【问题讨论】:
-
这不是我们对 Java 进行基准测试的方式。但是,是的,
Stream比简单的循环复杂得多——在这种微不足道的情况下它会更慢。 -
这是一个典型的比一文不值的基准;它比一文不值更糟糕,因为它会产生一个让你认为它有意义的数字。实际上,流通常与传统循环一样快,有时比传统循环快,有时比传统循环慢。而且,如果您正在迭代具有少于数百万个元素的数据集,那么它可能对整体程序性能根本不重要。编写清晰可维护的代码,性能几乎总是足够好。
-
伙计们,这根本不是基准!这是问题。请告诉我我错了,getFirstByStream 的性能真的比 getFirstByLoop 好,以任何对你信任的方式,我们会完成的。或者请告诉我什么时候流的性能更高,并且应该优先考虑性能。谢谢。
-
@engilyin 这就是 point,您必须针对 given 场景进行测量;然后使用分析器测量您的应用程序并证明流是弱点;最后一点是你应该正确地测量,这并不容易。但是 Brian Goetz 回答您的问题(Oracle/Java 的架构师)对您来说还不够吗?
-
作者这种懒惰的意思是,你的理解根本上是错误的。流解决方案不会链接多个循环。因此,您对性能的所有假设也是错误的。
标签: performance java-8 functional-programming java-stream