作为技术人员,学习新知识是基本功课。有些知识是不得不学,有些知识是学了之后如虎添翼,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列表,列表中随机生成10000000个字符串,然后分别通过不同的方式计算获得最小的字符串。

基本操作与实验一相同,不再贴出代码,直接看测试的效果图。

针对对象(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针对其进行优化,便同时享受了便捷和性能,何乐而不为呢。

原文链接《Java8 Stream性能如何及评测工具推荐

Java8 Stream性能如何及评测工具推荐的更多相关文章

  1. 【web性能】web性能测试工具推荐

    WEB性能测试工具主要分为三种,一种是测试页面资源加载速度的,一种是测试页面加载完毕后页面呈现.JS操作速度的,还有一种是总体上对页面进行评价分析,下面分别对这些工具进行介绍,如果谁有更好的工具也请一 ...

  2. TCP协议的性能评测工具 — Tcpdive开源啦

    Github地址:https://github.com/fastos/tcpdive 为什么要开发Tcpdive 在过去的几年里,随着移动互联网的飞速发展,整个基础网络已经发生了翻天覆地的变化. 用户 ...

  3. C和指针 第十八章 性能评测工具gprof

    linux平台下的gprof评测工具可以对程序进行分析,需要在编译时加上-pg选项,如上一章的二叉树代码: gcc -pg main.c ArrayBinaryTree.h ArrayBinaryTr ...

  4. 简洁又快速地处理集合——Java8 Stream(下)

    上一篇文章我讲解 Stream 流的基本原理,以及它与集合的区别关系,讲了那么多抽象的,本篇文章我们开始实战,讲解流的各个方法以及各种操作 没有看过上篇文章的可以先点击进去学习一下 简洁又快速地处理集 ...

  5. 如何通过 IntelliJ IDEA 来提升 Java8 Stream 的编码效率

    本文翻译整理自:https://winterbe.com/posts/2015/03/05/fixing-java-8-stream-gotchas-with-intellij-idea 作者:@Wi ...

  6. 如何用Java8 Stream API找到心仪的女朋友

    传统的的Java 集合操作是有些啰嗦的,当我们需要对结合元素进行过滤,排序等操作的时候,通常需要写好几行代码以及定义临时变量. 而Java8 Stream API 可以极大简化这一操作,代码行数少,且 ...

  7. 快速掌握Java8 Stream函数式编程技巧

    函数式编程优势 "函数第一位",即函数可以出现在任何地方. 可以把函数作为参数传递给另一个函数,还可以将函数作为返回值. 让代码的逻辑更清晰更优雅. 减少了可变量(Immutabl ...

  8. Java8 Stream 的最佳实践

    Java8 Stream 的最佳实践 java8stream提供了对于集合类的流失处理,其具有以下特点: Lazy Evaluation(长度可以无限) 只能使用一次 内部迭代 Lazy Evalua ...

  9. 《深入理解Java虚拟机》虚拟机性能监控与故障处理工具

    上节学习回顾 从课本章节划分,<垃圾收集器>和<内存分配策略>这两篇随笔同属一章节,主要是从理论+实验的手段来讲解JVM的内存处理机制.好让我们对JVM运行机制有一个良好的概念 ...

随机推荐

  1. Trace 2018徐州icpc网络赛 思维+二分

    There's a beach in the first quadrant. And from time to time, there are sea waves. A wave ( xx , yy) ...

  2. UVA - 10480 Sabotage 最小割,输出割法

    UVA - 10480 Sabotage 题意:现在有n个城市,m条路,现在要把整个图分成2部分,编号1,2的城市分成在一部分中,拆开每条路都需要花费,现在问达成目标的花费最少要隔开那几条路. 题解: ...

  3. Redis字符串键的底层原理

    before C语言基础 Redis基础 导入 redis的命令如下: set x "hello"; get x; hello Redis作为一种存储字符串的缓存结构,其具体实现是 ...

  4. (转)java程序调用内存变化过程分析(详细)

    原博地址: https://blog.csdn.net/Myuhua/article/details/81385609 (一)不含静态变量的java程序运行时内存变化过程分析 代码: package ...

  5. Protostuff序列化问题

    最近在开发中遇到一个Protostuff序列化问题,在这记录一下问题的根源:分析一下Protostuff序列化和反序列化原理:以及怎么样避免改bug. 1. 问题描述 有一个push业务用到了mq,m ...

  6. kafka 主题管理

    对于 kafka 主题(topic)的管理(增删改查),使用最多的便是kafka自带的脚本. 创建主题 kafka提供了自带的 kafka-topics 脚本,用来帮助用户创建主题(topic). b ...

  7. Servlet跳转方式sendReDirect()和forward()

    在web应用服务中,经常会面对不同SERVLET之间的跳转,目前我们可以通过以下两种方式实现: 1.RequestDispatcher.forward() 2.ServletResponse.send ...

  8. Net基础篇_学习笔记_第十一天_面向对象(类)

    类语法:[public] class 类名{ 字段; 属性; 方法;}写好了一个类之后,我们需要创建这个类的对象,那么,我们管创建这个类的对象过程称之为类的实例化.使用关键字 new. this:表示 ...

  9. Redis常用命令(key、string、List)

    1.Key 1.keys *   查询所有数据 2.exists key名   判断key名是否存在 3.move key名  数据库号(0-15)  移动数据key名到相应的数据库 4.expire ...

  10. 怎么在本地建立一个Maven 项目push到码云(https://git.oschina.net)

    本地建立一个的mvan项目不使用SmartGit push到码云上. 1 首先在自己码云的建立一个maven 空项目 2 然后打开STS(Spring Tool Suite)   新建一个Maven( ...