Java 在性能方面,Lambda和simple循环哪个更好?
我已经很快地阅读了文档 不过,这种例子帮助我更好地理解:Java 在性能方面,Lambda和simple循环哪个更好?,java,lambda,java-8,Java,Lambda,Java 8,我已经很快地阅读了文档 不过,这种例子帮助我更好地理解: //Old way: List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7); for(Integer n: list) { System.out.println(n); } //New way: List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7); list.forEach(n -> Sys
//Old way:
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7);
for(Integer n: list) {
System.out.println(n);
}
//New way:
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7);
list.forEach(n -> System.out.println(n));
//or we can use :: double colon operator in Java 8
list.forEach(System.out::println);
//旧方法:
List=Arrays.asList(1,2,3,4,5,6,7);
for(整数n:list){
系统输出println(n);
}
//新方式:
List=Arrays.asList(1,2,3,4,5,6,7);
list.forEach(n->System.out.println(n));
//或者我们可以在Java8中使用::双冒号运算符
list.forEach(System.out::println);
不过,我不明白为什么这是一种创新。当“方法变量”结束时,它只是一个死亡的方法,对吗?为什么我要用这个而不是真正的方法?
就性能而言,哪一个是更好的选择。Lambda或简单循环。就性能而言,普通函数比Lambda更好,因为groovy中存在与Lambda类似的闭包 这些东西的工作方式类似于,如果您为任何集合编写闭包,它将在内部创建另一个类,该类实际上对所提到的闭包或lambda表达式执行操作
但是,通过使用lambda和闭包,我可以以更好的方式进行迭代,并且可以轻松地进行调试。你可以写更少的代码行 在代码中使用lambda时,您是在告诉应该做什么,而不是如何做。上面的代码传递了一个方法参考,而不是在列表上循环,我认为lambda更符合人体工程学 就性能而言,打印100万个项目的列表几乎花费了相同的时间,但我没有在其他类型的操作中做基准测试 上面的代码是一个简单的操作,但是lambda有很多优点,因为您可以拥有可以传递的函数,使用多核(并行流)很容易等等 为什么我要用这个而不是真正的方法 你不应该。使用你更喜欢的方法
至于性能,我想,所有这些版本的速度大致相同。这里的I/O操作(
println
)比调用lambda或创建迭代器的所有可能开销都慢得多。一般来说,forEach
可能会稍微快一点,因为它在单个方法中执行所有操作,而无需创建迭代器
并调用hasNext
和next
(这是由for each循环隐式完成的)。无论如何,这取决于许多因素,例如调用此代码的频率、列表的长度、JIT编译器是否成功地对迭代器和/或lambda进行了反虚拟化等等。它允许您在一行(同时具有足够的可读性)中编写以前不可能的内容。性能在这里不是问题O(n)
停留O(n)
例如,在我的Windows Phone应用程序中,我有会话,其中会话是表演者,我希望选择所有具有一个具体表演者的会话(就像您希望看到一些演员在其中播放的所有电影一样)。在Java1.7或更低版本中,我必须使用内部循环创建循环,检查它,如果没有执行者,则返回null等。
使用lambda表达式,我可以做到:
//performerId is parameter passed by method before
Sessions.Where(x => x.Performers.Where(y => y.PerformerId == performerId).FirstOrDefault() != null)
现在在Java中也是如此(但我现在不在从事1.8项目,我没有Java示例,但我很期待它)
运行后输出为:
我的建议是:
- 让应用程序基准测试告诉您是否需要进行优化
- 让探查器指出代码中值得优化的部分
在这个特定的示例中,性能差异太小,无法测量。如果你放大到一个包含数百万个元素的列表,那么构建列表和写数字所需的时间将决定性能。不同的迭代方式只会对整体性能贡献一小部分
对于那些仍然想知道使用lambda还是传统循环更快的人(尽管有上述所有内容),最好的一般答案是: “这取决于各种各样的因素,1)没有被很好地理解,2)随着Java编译器技术的发展,可能会发生变化
我们可以为您提供一个关于特定Java major/minor/patch发行版的特定示例的答案,但概括起来是不明智的。如果您想了解lambda表达式的值,您不应该查看唯一一个具有以前存在的语言对应项的新方法。这些示例如何:
button.addActionListener( ev -> BACKGROUND_EXECUTOR_SERVICE.execute( () -> {
String result = longComputation();
SwingUtilities.invokeLater( () -> label.setText(result) );
});
只要想想等价的Java 8之前的代码是什么样子,您就会发现这里的主要优势不是性能
如果你想看看收集操作,这个如何
map.merge(key, 1, Integer::sum);
如果映射中还不存在键,则这会将1
放入映射中,否则会将1
添加到现有映射的值中。再次,请考虑旧映射的外观。如果映射具有适当的方法,只是一个额外的好处
当然,forEach
方法不能像for-e那样提供如此大的表现力增益
# Run complete. Total time: 00:00:34
Benchmark Mode Cnt Score Error Units
CheckLamdaPerformance.testPerDoubleColon thrpt 776.008 ops/s
CheckLamdaPerformance.testPerNew thrpt 1096.423 ops/s
CheckLamdaPerformance.testPerOld thrpt 873.542 ops/s
button.addActionListener( ev -> BACKGROUND_EXECUTOR_SERVICE.execute( () -> {
String result = longComputation();
SwingUtilities.invokeLater( () -> label.setText(result) );
});
map.merge(key, 1, Integer::sum);
Map<ContainerOrderFocusTraversalPolicy, List<AttributedCharacterIterator>> map;
//…
map.forEach((p,l)->l.forEach(i->{ /* do your work using p and i */}));
for(Map.Entry<ContainerOrderFocusTraversalPolicy, List<AttributedCharacterIterator>> e:
map.entrySet()) {
ContainerOrderFocusTraversalPolicy p=e.getKey();
for(AttributedCharacterIterator i: e.getValue()) {
/* do your work using p and i */
}
}
// Group employees by department
Map<Department, List<Employee>> byDept
= employees.stream()
.collect(Collectors.groupingBy(Employee::getDepartment));