Java8 Lambda表达式和流操作如何让你的代码变慢5倍
作者:网络转载 发布时间:[ 2015/12/21 10:38:17 ] 推荐标签:测试开发技术 编程语言
有许许多多关于 Java 8 中流效率的讨论,但根据 Alex Zhitnitsky 的测试结果显示:坚持使用传统的 Java 编程风格——iterator 和 for-each 循环——比 Java 8 的实现性能更佳。
Java 8 中的 Lambda 表达式和流(Stream)受到了热烈欢迎。这是 Java 迄今为止令人激动的特征。这些新的语言特征允许采用函数式风格来进行编码,我们可以用这些特性完成许多有趣的功能。这些特性如此有趣以至于被认为是不合理的。我们对此表示怀疑,于是决定对这些特性进行测试。
我们创建一个简单的任务:从一个 ArrayList 找出大值,将传统方式与 Java 8 中的新方式进行测试比较。说实话,测试的结果让我感到非常惊讶。
命令式风格与 Java 8 函数式编程风格比较
我喜欢直接进入主题,所以先看一下结果。为了做这次基准测试,我们先创建了一个 ArrayList,并插入一个 100000 个随机整数,并通过 7 种不同的方式遍历所有的值来查找大值。实现分为两组:Java 8 中引入的函数式风格与 Java 一直使用的命令式风格。
这是每个方法耗费的时长:
大错误记录是并行流上的 0.042,完整输出结果在这篇文章结尾部分可以看到。
小贴士:
哇哦!Java 8 中提供的任何一种新方式都会产生约 5 倍的性能差异。有时使用简单迭代器循环比混合 lambda 表达式和流更有效,即便这样需要多写几行代码,且需要跳过甜蜜的语法糖(syntactic suger)。
使用迭代器或 for-each 循环是遍历 ArrayList 有效的方式,性能比采用索引值的传统 for 循环方式好两倍。
在 Java 8 的方法中,并行流的性能佳。但是请小心,在某些情况下它也可能会导致程序运行得更慢。
Lambda 表达式的速度介于流与并行流之间。这个结果确实挺令人惊讶的,因为 lambda 表达式的实现方式是基于流的 API 来实现的。
不是所有的情况都如上所示:当我们想演示在 lambda 表达式和流中很容易犯错时,我们收到了很多社区的反馈,要求我们优化基准测试代码,如消除整数的自动装包和解包操作。第二次测试(已优化)的结果在这篇文章结束位置可以看到。
让我们快速看一下每个方法,按照运行速度由快到慢:
命令式风格
iteratorMaxInteger()——使用迭代器遍历列表:
public int iteratorMaxInteger() {
int max = Integer.MIN_VALUE;
for (Iterator it = integers.iterator(); it.hasNext(); ) {
max = Integer.max(max, it.next());
}
return max;
}
forEachLoopMaxInteger()——不使用迭代器,使用 For-Each 循环遍历列表(不要误用 Java 8 的 forEach)
public int forEachLoopMaxInteger() {
int max = Integer.MIN_VALUE;
for (Integer n : integers) {
max = Integer.max(max, n);
}
return max;
}
forMaxInteger()——使用简单的 for 循环和索引遍历列表:
public int forMaxInteger() {
int max = Integer.MIN_VALUE;
for (int i = 0; i < size; i++) {
max = Integer.max(max, integers.get(i));
}
return max;
}
函数式风格
parallelStreamMaxInteger()——使用 Java 8 并行流遍历列表:
public int parallelStreamMaxInteger() {
Optional max = integers.parallelStream().reduce(Integer::max);
return max.get();
}
lambdaMaxInteger()——使用 lambda 表达式及流遍历列表。优雅的一行代码:
public int lambdaMaxInteger() {
return integers.stream().reduce(Integer.MIN_VALUE, (a, b) -> Integer.max(a, b));
}
forEachLambdaMaxInteger()——这个用例有点混乱。可能是因为 Java 8 的 forEach 特性有一个很烦人的东西:只能使用 final 变量,所以我们创建一个 final 包装类来解决该问题,这样我们能访问到更新后的大值。
public int forEachLambdaMaxInteger() {
final Wrapper wrapper = new Wrapper();
wrapper.inner = Integer.MIN_VALUE;
integers.forEach(i -> helper(i, wrapper));
return wrapper.inner.intValue();
}
public static class Wrapper {
public Integer inner;
}
private int helper(int i, Wrapper wrapper) {
wrapper.inner = Math.max(i, wrapper.inner);
return wrapper.inner;
}
相关推荐
更新发布
功能测试和接口测试的区别
2023/3/23 14:23:39如何写好测试用例文档
2023/3/22 16:17:39常用的选择回归测试的方式有哪些?
2022/6/14 16:14:27测试流程中需要重点把关几个过程?
2021/10/18 15:37:44性能测试的七种方法
2021/9/17 15:19:29全链路压测优化思路
2021/9/14 15:42:25性能测试流程浅谈
2021/5/28 17:25:47常见的APP性能测试指标
2021/5/8 17:01:11