一、粉丝的反馈

问:stream比for循环慢5倍,用这个是为了啥?

答:互联网是一个新闻泛滥的时代,三人成虎,以假乱真的事情时候发生。作为一个技术开发者,要自己去动手去做,不要人云亦云。

的确,这位粉丝说的这篇文章我也看过,我就不贴地址了,也没必要给他带流量。怎么说呢?就是一个不懂得测试的、不入流开发工程师做的性能测试,给出了一个危言耸听的结论。

二、所有性能测试结论都是片面的

性能测试是必要的,但针对性能测试的结果,永远要持怀疑态度。为什么这么说?

  • 性能测试脱离业务场景就是片面的性能测试。你能覆盖所有的业务场景么?
  • 性能测试脱离硬件环境就是片面的性能测试。你能覆盖所有的硬件环境么?
  • 性能测试脱离开发人员的知识面就是片面的性能测试。你能覆盖各种开发人员奇奇怪怪的代码么?

所以,我从来不相信网上的任何性能测试的文章。凡是我自己的从事的业务场景,我都要在接近生产环境的机器上自己测试一遍。 所有性能测试结论都是片面的,只有你生产环境下的运行结果才是真的。

三、动手测试Stream的性能

3.1.环境

windows10 、16G内存、i7-7700HQ 2.8HZ 、64位操作系统、JDK 1.8.0_171

3.2.测试用例与测试结论

我们在上一节,已经讲过:

  • 针对不同的数据结构,Stream流的执行效率是不一样的
  • 针对不同的数据源,Stream流的执行效率也是不一样的

所以记住笔者的话:所有性能测试结论都是片面的,你要自己动手做,相信你自己的代码和你的环境下的测试!我的测试结果仅仅代表我自己的测试用例和测试数据结构!

3.2.1.测试用例一

测试用例:5亿个int随机数,求最小值

测试结论(测试代码见后文):

  • 使用普通for循环,执行效率是Stream串行流的2倍。也就是说普通for循环性能更好。
  • Stream并行流计算是普通for循环执行效率的4-5倍。
  • Stream并行流计算 > 普通for循环 > Stream串行流计算

3.2.测试用例二

测试用例:长度为10的1000000随机字符串,求最小值

测试结论(测试代码见后文):

  • 普通for循环执行效率与Stream串行流不相上下
  • Stream并行流的执行效率远高于普通for循环
  • Stream并行流计算 > 普通for循环 = Stream串行流计算

3.3.测试用例三

测试用例:10个用户,每人200个订单。按用户统计订单的总价。

测试结论(测试代码见后文):

  • Stream并行流的执行效率远高于普通for循环
  • Stream串行流的执行效率大于等于普通for循环
  • Stream并行流计算 > Stream串行流计算 >= 普通for循环

四、最终测试结论

  • 对于简单的数字(list-Int)遍历,普通for循环效率的确比Stream串行流执行效率高(1.5-2.5倍)。但是Stream流可以利用并行执行的方式发挥CPU的多核优势,因此并行流计算执行效率高于for循环。
  • 对于list-Object类型的数据遍历,普通for循环和Stream串行流比也没有任何优势可言,更不用提Stream并行流计算。

虽然在不同的场景、不同的数据结构、不同的硬件环境下。Stream流与for循环性能测试结果差异较大,甚至发生逆转。但是总体上而言

  • Stream并行流计算 >> 普通for循环 ~= Stream串行流计算 (之所以用两个大于号,你细品)
  • 数据容量越大,Stream流的执行效率越高。
  • Stream并行流计算通常能够比较好的利用CPU的多核优势。CPU核心越多,Stream并行流计算效率越高。

stream比for循环慢5倍?也许吧,单核CPU、串行Stream的int类型数据遍历?我没试过这种场景,但是我知道这不是应用系统的核心场景。看了十几篇测试博文,和我的测试结果。我的结论是: 在大多数的核心业务场景下及常用数据结构下,Stream的执行效率比for循环更高。 毕竟我们的业务中通常是实实在在的实体对象,没事谁总对List<Int>类型进行遍历?谁的生产服务器是单核?。

五、测试代码

  1. <dependency>
  2. <groupId>com.github.houbb</groupId>
  3. <artifactId>junitperf</artifactId>
  4. <version>2.0.0</version>
  5. </dependency>

测试用例一:

  1. import com.github.houbb.junitperf.core.annotation.JunitPerfConfig;
  2. import com.github.houbb.junitperf.core.report.impl.HtmlReporter;
  3. import org.junit.jupiter.api.BeforeAll;
  4. import java.util.Arrays;
  5. import java.util.Random;
  6. public class StreamIntTest {
  7. public static int[] arr;
  8. @BeforeAll
  9. public static void init() {
  10. arr = new int[500000000]; //5亿个随机Int
  11. randomInt(arr);
  12. }
  13. @JunitPerfConfig( warmUp = 1000, reporter = {HtmlReporter.class})
  14. public void testIntFor() {
  15. minIntFor(arr);
  16. }
  17. @JunitPerfConfig( warmUp = 1000, reporter = {HtmlReporter.class})
  18. public void testIntParallelStream() {
  19. minIntParallelStream(arr);
  20. }
  21. @JunitPerfConfig( warmUp = 1000, reporter = {HtmlReporter.class})
  22. public void testIntStream() {
  23. minIntStream(arr);
  24. }
  25. private int minIntStream(int[] arr) {
  26. return Arrays.stream(arr).min().getAsInt();
  27. }
  28. private int minIntParallelStream(int[] arr) {
  29. return Arrays.stream(arr).parallel().min().getAsInt();
  30. }
  31. private int minIntFor(int[] arr) {
  32. int min = Integer.MAX_VALUE;
  33. for (int anArr : arr) {
  34. if (anArr < min) {
  35. min = anArr;
  36. }
  37. }
  38. return min;
  39. }
  40. private static void randomInt(int[] arr) {
  41. Random r = new Random();
  42. for (int i = 0; i < arr.length; i++) {
  43. arr[i] = r.nextInt();
  44. }
  45. }
  46. }

测试用例二:

  1. import com.github.houbb.junitperf.core.annotation.JunitPerfConfig;
  2. import com.github.houbb.junitperf.core.report.impl.HtmlReporter;
  3. import org.junit.jupiter.api.BeforeAll;
  4. import java.util.ArrayList;
  5. import java.util.Random;
  6. public class StreamStringTest {
  7. public static ArrayList<String> list;
  8. @BeforeAll
  9. public static void init() {
  10. list = randomStringList(1000000);
  11. }
  12. @JunitPerfConfig(duration = 10000, warmUp = 1000, reporter = {HtmlReporter.class})
  13. public void testMinStringForLoop(){
  14. String minStr = null;
  15. boolean first = true;
  16. for(String str : list){
  17. if(first){
  18. first = false;
  19. minStr = str;
  20. }
  21. if(minStr.compareTo(str)>0){
  22. minStr = str;
  23. }
  24. }
  25. }
  26. @JunitPerfConfig(duration = 10000, warmUp = 1000, reporter = {HtmlReporter.class})
  27. public void textMinStringStream(){
  28. list.stream().min(String::compareTo).get();
  29. }
  30. @JunitPerfConfig(duration = 10000, warmUp = 1000, reporter = {HtmlReporter.class})
  31. public void testMinStringParallelStream(){
  32. list.stream().parallel().min(String::compareTo).get();
  33. }
  34. private static ArrayList<String> randomStringList(int listLength){
  35. ArrayList<String> list = new ArrayList<>(listLength);
  36. Random rand = new Random();
  37. int strLength = 10;
  38. StringBuilder buf = new StringBuilder(strLength);
  39. for(int i=0; i<listLength; i++){
  40. buf.delete(0, buf.length());
  41. for(int j=0; j<strLength; j++){
  42. buf.append((char)('a'+ rand.nextInt(26)));
  43. }
  44. list.add(buf.toString());
  45. }
  46. return list;
  47. }
  48. }

测试用例三:

  1. import com.github.houbb.junitperf.core.annotation.JunitPerfConfig;
  2. import com.github.houbb.junitperf.core.report.impl.HtmlReporter;
  3. import org.junit.jupiter.api.BeforeAll;
  4. import java.util.*;
  5. import java.util.stream.Collectors;
  6. public class StreamObjectTest {
  7. public static List<Order> orders;
  8. @BeforeAll
  9. public static void init() {
  10. orders = Order.genOrders(10);
  11. }
  12. @JunitPerfConfig(duration = 10000, warmUp = 1000, reporter = {HtmlReporter.class})
  13. public void testSumOrderForLoop(){
  14. Map<String, Double> map = new HashMap<>();
  15. for(Order od : orders){
  16. String userName = od.getUserName();
  17. Double v;
  18. if((v=map.get(userName)) != null){
  19. map.put(userName, v+od.getPrice());
  20. }else{
  21. map.put(userName, od.getPrice());
  22. }
  23. }
  24. }
  25. @JunitPerfConfig(duration = 10000, warmUp = 1000, reporter = {HtmlReporter.class})
  26. public void testSumOrderStream(){
  27. orders.stream().collect(
  28. Collectors.groupingBy(Order::getUserName,
  29. Collectors.summingDouble(Order::getPrice)));
  30. }
  31. @JunitPerfConfig(duration = 10000, warmUp = 1000, reporter = {HtmlReporter.class})
  32. public void testSumOrderParallelStream(){
  33. orders.parallelStream().collect(
  34. Collectors.groupingBy(Order::getUserName,
  35. Collectors.summingDouble(Order::getPrice)));
  36. }
  37. }
  38. class Order{
  39. private String userName;
  40. private double price;
  41. private long timestamp;
  42. public Order(String userName, double price, long timestamp) {
  43. this.userName = userName;
  44. this.price = price;
  45. this.timestamp = timestamp;
  46. }
  47. public String getUserName() {
  48. return userName;
  49. }
  50. public double getPrice() {
  51. return price;
  52. }
  53. public long getTimestamp() {
  54. return timestamp;
  55. }
  56. public static List<Order> genOrders(int listLength){
  57. ArrayList<Order> list = new ArrayList<>(listLength);
  58. Random rand = new Random();
  59. int users = listLength/200;// 200 orders per user
  60. users = users==0 ? listLength : users;
  61. ArrayList<String> userNames = new ArrayList<>(users);
  62. for(int i=0; i<users; i++){
  63. userNames.add(UUID.randomUUID().toString());
  64. }
  65. for(int i=0; i<listLength; i++){
  66. double price = rand.nextInt(1000);
  67. String userName = userNames.get(rand.nextInt(users));
  68. list.add(new Order(userName, price, System.nanoTime()));
  69. }
  70. return list;
  71. }
  72. @Override
  73. public String toString(){
  74. return userName + "::" + price;
  75. }
  76. }

欢迎关注我的博客,里面有很多精品合集

  • 本文转载注明出处(必须带连接,不能只转文字):字母哥博客

觉得对您有帮助的话,帮我点赞、分享!您的支持是我不竭的创作动力! 。另外,笔者最近一段时间输出了如下的精品内容,期待您的关注。

恕我直言你可能真的不会java第6篇:Stream性能差?不要人云亦云的更多相关文章

  1. 恕我直言你可能真的不会java第11篇-Stream API终端操作

    一.Java Stream管道数据处理操作 在本号之前写过的文章中,曾经给大家介绍过 Java Stream管道流是用于简化集合类元素处理的java API.在使用的过程中分为三个阶段.在开始本文之前 ...

  2. 恕我直言你可能真的不会java第9篇-Stream元素的匹配与查找

    在我们对数组或者集合类进行操作的时候,经常会遇到这样的需求,比如: 是否包含某一个"匹配规则"的元素 是否所有的元素都符合某一个"匹配规则" 是否所有元素都不符 ...

  3. 恕我直言你可能真的不会java第1篇:lambda表达式会用了么?

    本文配套教学视频:B站观看地址 在本号之前写过的一些文章中,笔者使用了lambda表达式语法,一些读者反映说代码看不懂.本以为java 13都已经出了,java 8中最重要特性lambda表达式大家应 ...

  4. 恕我直言你可能真的不会java第2篇:Java Stream API?

    一.什么是Java Stream API? Java Stream函数式编程接口最初是在Java 8中引入的,并且与lambda一起成为Java开发的里程碑式的功能特性,它极大的方便了开放人员处理集合 ...

  5. 恕我直言你可能真的不会java第4篇:Stream管道流Map操作

    一.回顾Stream管道流map的基础用法 最简单的需求:将集合中的每一个字符串,全部转换成大写! List<String> alpha = Arrays.asList("Mon ...

  6. 恕我直言你可能真的不会java第7篇:像使用SQL一样排序集合

    在开始之前,我先卖个关子提一个问题:我们现在有一个Employee员工类. @Data @AllArgsConstructor public class Employee { private Inte ...

  7. 恕我直言你可能真的不会java第8篇-函数式接口

    一.函数式接口是什么? 所谓的函数式接口,实际上就是接口里面只能有一个抽象方法的接口.我们上一节用到的Comparator接口就是一个典型的函数式接口,它只有一个抽象方法compare. 只有一个抽象 ...

  8. 恕我直言你可能真的不会java第12篇-如何使用Stream API对Map类型元素排序

    在这篇文章中,您将学习如何使用Java对Map进行排序.前几日有位朋友面试遇到了这个问题,看似很简单的问题,但是如果不仔细研究一下也是很容易让人懵圈的面试题.所以我决定写这样一篇文章.在Java中,有 ...

  9. 恕我直言你可能真的不会java第3篇:Stream的Filter与谓词逻辑

    一.基础代码准备 建立一个实体类,该实体类有五个属性.下面的代码使用了lombok的注解Data.AllArgsConstructor,这样我们就不用写get.set方法和全参构造函数了.lombok ...

随机推荐

  1. Django ListView DetailView等基于类的视图如何添加装饰器?

    场景: Django开发中,如果我们使用了类视图,如:ListView.DetailView.UpdateView等,这时我们又想要对这个视图添加一个装饰器,来实现某种功能,这时候该怎么处理呢? 环境 ...

  2. JavaScript 简版-菜鸟中的菜鸟2

    JavaScript 输出 JavaScript 没有任何打印或者输出的函数. JavaScript 显示数据 JavaScript 可以通过不同的方式来输出数据: 使用 window.alert() ...

  3. 拨号vps,拨号vps是什么意思干什么用的,如何使用拨号vps

    首先,拨号vps是动态IP的VPS.vps虚拟服务器.拨号服务器.有些业务,如刷单.投票等操作对ip地址有限制,不能过多的使用.而拨号VPS通过拨号上网,每拨号一次号,就变一次IP,完成ip地址的动态 ...

  4. POJ 2671 Jimmy's Bad Day题解(很详细很友好,类似区间dp)

    有问题的话欢迎在评论区提出 题意: 题目链接 你是一个送快递的,现在给你一个环,环的边有权值,代表走这条边所花的时间,每个点代表一个地点,点有点权,代表这个点上有多少货物需要你送.初始时间\(t=0\ ...

  5. Docker 容器优雅终止方案

    原文链接:Docker 容器优雅终止方案 作为一名系统重启工程师(SRE),你可能经常需要重启容器,毕竟 Kubernetes 的优势就是快速弹性伸缩和故障恢复,遇到问题先重启容器再说,几秒钟即可恢复 ...

  6. Java实现 蓝桥杯VIP 算法提高 项链

    算法提高 项链 时间限制:1.0s 内存限制:512.0MB 问题描述 由 n(1≤n≤100) 个珠子组成的一个项链,珠子有红.蓝.白三种颜色,各种颜色的珠子的安排顺序由键盘输入的字符串任意给定.蓝 ...

  7. Java实现 LeetCode 58 最后一个单词的长度

    58. 最后一个单词的长度 给定一个仅包含大小写字母和空格 ' ' 的字符串 s,返回其最后一个单词的长度. 如果字符串从左向右滚动显示,那么最后一个单词就是最后出现的单词. 如果不存在最后一个单词, ...

  8. java实现杨辉三角系数

    ** 杨辉三角系数** (a+b)的n次幂的展开式中各项的系数很有规律,对于n=2,3,4时分别是:1 2 1, 1 3 3 1,1 4 6 4 1.这些系数构成了著名的杨辉三角形: 1 1 1 1 ...

  9. java实现第八届蓝桥杯树型显示

    树型显示 题目描述 对于分类结构可以用树形来形象地表示.比如:文件系统就是典型的例子. 树中的结点具有父子关系.我们在显示的时候,把子项向右缩进(用空格,不是tab),并添加必要的连接线,以使其层次关 ...

  10. 温故知新-java虚拟机

    文章目录 java虚拟机是什么? jvm的体系结构 第一个类加载子系统 类的生命周期 加载器分类 类加载机制 第二个运行时数据区(内存结构) GC算法和收集器 如何判断对象可以被回收? 如何判断一个常 ...