【发布时间】:2014-01-21 02:14:28
【问题描述】:
我有需要遍历的列表。我使用的是 Java 8 中的新 foreach。我发现它比旧的不太优雅的方法慢得多。我读过this 堆栈讨论,它更多地关注最佳实践,而不是现实世界的实施。我知道使用 lambda 时方法传递会产生额外的开销,但我仍然不明白第一种情况和第三种情况之间的差异,这让我认为我可能用错了。是否还没有对 lambda 进行优化,或者是否有更好的方法可以使用它们来获得更高的性能?下面是一个演示示例,而不是我的实际代码。
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
public class Main {
public static void main(String[] args) {
List<Memes> memesList =
Arrays.asList(
new OldMemes(),
new TrendingMemes()
);
//----------------------------Benchmark
long start, end;
start = System.nanoTime();
System.out.format("for each started @%d\n", start);
//Old way.
for(Memes memes : memesList){
memes.showMeme();
}
//-----------------------------Benchmark
end = System.nanoTime();
System.out.println((end - start));
start = System.nanoTime();
System.out.format("for started @%d\n", start);
//Other way
for(Iterator<Memes> i = memesList.iterator(); i.hasNext(); ) {
i.next().showMeme();
}
//-----------------------------Benchmark
end = System.nanoTime();
System.out.println((end - start));
start = System.nanoTime();
System.out.format("for each lambda started @%d\n", start);
//New way.
memesList.forEach((memes) -> memes.showMeme());
//-----------------------------Benchmark
end = System.nanoTime();
System.out.println((end - start));
System.out.format("end @%d\n", end);
}
interface Memes {
public void showMeme();
}
static class OldMemes implements Memes {
@Override
public void showMeme() {
System.out.println("Id appetere senserit his, nonumes consulatu vel id.");
}
}
static class TrendingMemes implements Memes {
@Override
public void showMeme() {
System.out.println("Id appetere senserit his, nonumes consulatu vel id.");
}
}
}
【问题讨论】:
-
您的基准测试有缺陷 + 大部分时间都花在打印的
showMeme方法上(相对于迭代而言,这是一个非常缓慢的操作)... -
@assylias 我知道基准测试是有缺陷的,根据下面的答案。您是说 Java 8 forEach 在内部发生缓慢操作时会比其他方法慢,还是说使用有缺陷的基准无法判断哪个更慢?
-
我是说你的基准测试没有衡量你认为它正在衡量的东西,所以我认为你不能从中得出任何结论。
-
在需要实例化 lambda 时第一次执行一段代码与刚刚重用 lambda 时执行该代码的所有后续执行之间存在差异。这就像第一次使用一个类;它必须被加载和验证,测量时间并不能说明您使用实例的循环。这就是为什么在进行基准测试时应该多次执行代码并丢弃第一次执行的结果的原因。在您的示例中使用迭代次数很少的循环会使情况变得更糟。
-
肯定会更慢,因为带有 lambdas 的流 API 设置了很多脚手架代码。换句话说,lambdas 要慢得多。尽管您的测试存在缺陷,但现实生活中可悲的结论是,与正常使用迭代器或带索引的 for 循环相比,Streams api 中的`forEach` 非常慢。
标签: java optimization foreach lambda java-8