我是Java 8的新手,对Lambda Expression的范围感到有点困惑。我读了一些文章,表示Lambda Expression减少了执行时间,所以要找到我写的两个程序之后
1)不使用Lambda Expression
import java.util.*;
public class testing_without_lambda
{
public static void main(String args[])
{
long startTime = System.currentTimeMillis();
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6);
for (int number : numbers)
{
System.out.println(number);
}
long stopTime = System.currentTimeMillis();
System.out.print("without lambda:");
System.out.println(stopTime - startTime);
}//end main
}
输出:
2)使用Lambda Expression
import java.util.*;
public class testing_with_lambda
{
public static void main(String args[])
{
long startTime = System.currentTimeMillis();
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6);
numbers.forEach((Integer value) -> System.out.println(value));
long stopTime = System.currentTimeMillis();
System.out.print("with lambda:");
System.out.print(stopTime - startTime);
}//end main
}
输出:
这是否意味着Lambda Expression需要更多时间来执行?
答案 0 :(得分:5)
没有关于“执行时间”的一般说法,因为即使术语“执行时间”并不总是意义相同。当然,没有理由,为什么只使用lambda表达式一般会减少执行时间。
您的代码正在测量代码的初始化时间和它的执行时间,这是公平的,当您考虑该小程序的总执行时间时,但对于现实生活中的应用程序,它没有相关性,因为它们通常运行< em>显着比初始化时间长。
初始化时间的巨大差异是JRE在内部使用Collection API本身的事实,因此在应用程序启动之前,它的类被加载和初始化甚至可能在某种程度上进行优化(所以你不要衡量其成本)。相反,它不使用lambda表达式,因此首次使用lambda表达式将在后台加载并初始化整个框架。
由于您通常对实际应用程序中的代码如何执行感兴趣,因为初始化已经发生,您必须在同一JVM中多次执行代码才能获得更好的图像。但是,允许JVM的优化器处理代码有可能由于其更简单的性质(与现实生活场景相比)而过度优化,并且显示过于乐观的数字。这就是为什么建议使用由专家开发的复杂基准测试工具,而不是创建自己的。即使使用这些工具,您也必须研究他们的文档以了解并避免陷阱。另请参阅How do I write a correct micro-benchmark in Java?
当您将for
循环(也称为“外部迭代”)与等效的forEach
调用(也称为“内部迭代”)进行比较时,后者确实具有提高效率的潜力,如果由特定的Collection正确实现,但其结果很难预测,因为JVM的优化器擅长消除其他解决方案的缺点。此外,如果您的列表存在,您的列表太小,无法显示出这种差异。
还必须强调的是,这一原则对于lambda表达式并不严格。您还可以通过匿名内部类实现Consumer
,在您的情况下,示例遇到第一次初始化成本时,匿名内部类将比lambda表达式更快。</ p>
答案 1 :(得分:3)
除Holger's回答之外,我想展示您如何对代码进行基准测试。
您真正测量的是类和系统IO的初始化(即类加载和System.out::println
)。
为了摆脱这些,你应该使用像JMH这样的基准框架。此外,您应该使用多个列表或数组大小进行测量。
然后您的代码可能如下所示:
@Fork(3)
@BenchmarkMode(Mode.AverageTime)
@Measurement(iterations = 10, timeUnit = TimeUnit.NANOSECONDS)
@State(Scope.Benchmark)
@Threads(1)
@Warmup(iterations = 5, timeUnit = TimeUnit.NANOSECONDS)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
public class MyBenchmark {
@Param({ "10", "1000", "10000" })
private int length;
private List<Integer> numbers;
@Setup
public void setup() {
Random random = new Random(length);
numbers = random.ints(length).boxed().collect(Collectors.toList());
}
@Benchmark
public void externalIteration(Blackhole bh) {
for (Integer number : numbers) {
bh.consume(number);
}
}
@Benchmark
public void internalIteration(Blackhole bh) {
numbers.forEach(bh::consume);
}
}
结果:
Benchmark (length) Mode Cnt Score Error Units
MyBenchmark.externalIteration 10 avgt 30 41,002 ± 0,263 ns/op
MyBenchmark.externalIteration 1000 avgt 30 4026,842 ± 71,318 ns/op
MyBenchmark.externalIteration 10000 avgt 30 40423,629 ± 572,055 ns/op
MyBenchmark.internalIteration 10 avgt 30 40,783 ± 0,815 ns/op
MyBenchmark.internalIteration 1000 avgt 30 3888,240 ± 28,790 ns/op
MyBenchmark.internalIteration 10000 avgt 30 41961,320 ± 991,047 ns/op
正如你所看到的,几乎没有区别。
答案 2 :(得分:0)
我不认为lambda表达式代码在执行过程中会更快,它实际上取决于不同的条件。您是否可以指向我阅读的文章,其中lambda表达式的执行时间更快? (由于代码的函数编程风格,在编写代码时当然会被认为更快)
我在当地再次测试,发现了一些奇怪的事情:
一般结果:
1 2 3 4 五 6 与lambda:47
1 2 3 4 五 6 没有lambda:1
我认为真的需要一个非常大的数字样本才能真正测试这个,并且多次调用相同的代码来消除JVM上的任何初始化负担。这是一个非常小的测试样本。