点击蓝色“ 程序猿DD ”关注我
回复“ 资源 ”获取独家整理的学习资料!
阿里云双十一大促登场,价格秒杀众友商
作为技术人员,学习新知识是基本功课。 有些知识是不得不学,有些知识是学了之后如虎添翼,Java8的Stream就是兼具两者的知识。 不学看不懂,学了写起代码来如虎添翼。
在上篇《 Java8 Stream新特性详解及实战 》中我们介绍了Java8 Stream的基本使用方法,尝试一下是不是感觉很爽? 当只用一行代码就搞定最终结果时,是不是再也不想用for循环一遍遍去迭代了。
同时,你是否又看到类似《Java8 Lambda表达式和流操作如何让你的代码变慢5倍》这样的文章,那么今天就带大家通过编写测试程序来一探究竟,看看Stream的性能到底如何。 同时,带大家认识一个非常不错的性能测试工具junitperf。
先同步一下测试环境及工具信息:
JDK版本: 1.8.0_151。
电脑配置: MacBook Pro i7,16G内存。
Java测试工具: junitperf及Junit。
IDE: intellij IDEA。
在测试的过程中电脑中还开了其他很多应用,但基本上都没进行操作。
基本测试方案,先初始化一个int数组,5亿个随机数。 然后从这个数组中找到最小的一个数。
采用三个单元测试方法来对照参考:
testIntFor方法 测试for循环执行时间;
testIntStream方法 测试串行Stream执行时间;
testIntParallelStream方法 测试并行Stream执行时间;
测试程序相关代码:
public class StreamTest {
public static int[] arr;
@BeforeAll
public static void init() {
arr = new int[500000000];
randomInt(arr);
}
@JunitPerfConfig(duration = 10000, warmUp = 1000, reporter = {HtmlReporter.class})
public void testIntFor() {
minIntFor(arr);
}
@JunitPerfConfig(duration = 10000, warmUp = 1000, reporter = {HtmlReporter.class})
public void testIntParallelStream() {
minIntParallelStream(arr);
}
@JunitPerfConfig(duration = 10000, warmUp = 1000, reporter = {HtmlReporter.class})
public void testIntStream() {
minIntStream(arr);
}
private int minIntStream(int[] arr) {
return Arrays.stream(arr).min().getAsInt();
}
private int minIntParallelStream(int[] arr) {
return Arrays.stream(arr).parallel().min().getAsInt();
}
private int minIntFor(int[] arr) {
int min = Integer.MAX_VALUE;
for (int anArr : arr) {
if (anArr < min) {
min = anArr;
}
}
return min;
}
private static void randomInt(int[] arr) {
Random r = new Random();
for (int i = 0; i < arr.length; i++) {
arr[i] = r.nextInt();
}
}
}
基本操作流程: 通过@BeforeAll注解的init方法对数组进行随机初始化,然后再统一执行上面三个方法。
在单元测试的方法上都有下面的注解:
@JunitPerfConfig(duration = 10000, warmUp = 1000, reporter = {HtmlReporter.class})
该注释为junitperf提供的注解,其中duration为持续执行这段代码的时间,单位毫秒; warmUp预热时间,这里预热1秒; reporter输出报表格式,这里采用HTML展示,可以更直观看到效果。
好上面的一切都准备好了,剩下的就是统一执行单元测试。 执行结果如下三个图。
针对基础类型(int)操作,结果分析:
串行Stream的执行的确不如for循环性能高,耗时大概是for循环的2倍。
并行Stream的执行性能要优于for循环,耗时大概是for循环的一半。
这里没有用不同核数的机器测试,但并行Stream随着服务器核数的增加,必然更快。
生成一个List列表,列表中随机生成1千万个字符串,然后分别通过不同的方式计算获得最小的字符串。 基本操作与实验一相同,不再贴出代码,直接看测试的效果图。
针对对象(String)操作,结果分析:
Stream的性能与for循环已经相差不大了,耗时大概是for循环的1.25倍左右。
并行Stream执行的性能要优于for循环,而且比基础类型的优势更高,耗时已经低于for循环的一半。
针对不同服务器核数,Stream效率同样会更加高。
生成一个List列表,列表里面存放着1百万个User对象。 每个对象中都包含用户名和用户某次运动的距离,同一用户可在List里包含多条运动记录。 现在通过不同的方式来统计用户的总共运动了多远距离。
基本测试思路一致,这里只贴出基于Stream的算法的代码,以便大家了解Stream的复杂对象归约如何使用。
// 串行写法
users.stream().collect(
Collectors.groupingBy(User::getUserName,
Collectors.summingDouble(User::getMeters)));
// 并行写法
users.parallelStream().collect(
Collectors.groupingBy(User::getUserName,
Collectors.summingDouble(User::getMeters)));
下面看测试结果的数据:
复杂对象归约操作,结果分析:
基于Stream的操作明显都高于for循环的效率,而且并行的效果更加明显。
同样,随着服务器核数的增加,并行Stream的效率会更高。
最后推荐一下这款用起来还不错的Java性能测试工具,GitHub地址: https://github.com/houbb/junitperf。 上面有详细的使用说明。 唯一缺少的就是数据预初始化的示例,而本篇文章的示例中已经补上了这部分缺失。
通过上面的几组实验对比,我们可以看到如下结论:
针对简单的操作,比如基础类型的遍历,使用for循环性能要明显高于串行Stream操作。 但Stream的并行操作随着服务器的核数增加,会优于for循环。
针对复杂操作,串行Stream性能与for循环不差上下,但并行Stream的性能已经是无法匹敌了。
特别是针对一个集合进行多层过滤并归约操作,无论从写法上或性能上都要明显优于for循环。
用一句话来说就是: 简单操作for循环即可,复杂操作首推Stream 。
现在的Stream书写简单,性能不错,如果未来JDK针对其进行优化,便同时享受了便捷和性能,何乐而不为呢。
本文通过OpenWrite的免费Markdown转换工具发布
留言交流不过瘾
关注我,回复“ 加群 ” 加入各种主题讨论群
效率至上: K8S一键部署了解一下?
Java 14 可能带来什么新特性?
面试最后一问:你有什么问题想问我吗?
做程序员的老婆,一定要注意的事情
Spring Boot 2.2.0 正式发布,大幅性能提
朕已阅