java stream 慢_恕我直言你可能真的不会java第6篇:Stream性能差?不要人云亦云

java stream 慢_恕我直言你可能真的不会java第6篇:Stream性能差?不要人云亦云一 粉丝的反馈 问 stream 比 for 循环慢 5 倍 用这个是为了啥 答 互联网是一个新闻泛滥的时代 三人成虎 以假乱真的事情时候发生 作为一个技术开发者 要自己去动手去做 不要人云亦云 的确 这位粉丝说的这篇文章我也看过

大家好,我是讯享网,很高兴认识大家。

一、粉丝的反馈

f6558899dd17f3da32ea1ea08f3d1d47.png
讯享网

问: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串行流计算

811eb44a530463ff107363a56d3c4598.png

3.2.测试用例二

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

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

普通for循环执行效率与Stream串行流不相上下

Stream并行流的执行效率远高于普通for循环

Stream并行流计算 > 普通for循环 = Stream串行流计算

c08b399bf21d3c93d51a2224d5486029.png

3.3.测试用例三

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

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

Stream并行流的执行效率远高于普通for循环

Stream串行流的执行效率大于等于普通for循环

Stream并行流计算 > Stream串行流计算 >= 普通for循环

c6a8de945ead351109f7e8aa45753c48.png

四、最终测试结论

对于简单的数字(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类型进行遍历?谁的生产服务器是单核?。

五、测试代码

com.github.houbb

junitperf

2.0.0

测试用例一:

import com.github.houbb.junitperf.core.annotation.JunitPerfConfig;

import com.github.houbb.junitperf.core.report.impl.HtmlReporter;

import org.junit.jupiter.api.BeforeAll;

import java.util.Arrays;

import java.util.Random;

public class StreamIntTest {

public static int[] arr;

@BeforeAll

public static void init() {

arr = new int[]; //5亿个随机Int

randomInt(arr);

}

@JunitPerfConfig( warmUp = 1000, reporter = {HtmlReporter.class})

public void testIntFor() {

minIntFor(arr);

}

@JunitPerfConfig( warmUp = 1000, reporter = {HtmlReporter.class})

public void testIntParallelStream() {

minIntParallelStream(arr);

}

@JunitPerfConfig( 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();

}

}

}

测试用例二:

import com.github.houbb.junitperf.core.annotation.JunitPerfConfig;

import com.github.houbb.junitperf.core.report.impl.HtmlReporter;

import org.junit.jupiter.api.BeforeAll;

import java.util.ArrayList;

import java.util.Random;

public class StreamStringTest {

public static ArrayList list;

@BeforeAll

public static void init() {

list = randomStringList();

}

@JunitPerfConfig(duration = 10000, warmUp = 1000, reporter = {HtmlReporter.class})

public void testMinStringForLoop(){

String minStr = null;

boolean first = true;

for(String str : list){

if(first){

first = false;

minStr = str;

}

if(minStr.compareTo(str)>0){

minStr = str;

}

}

}

@JunitPerfConfig(duration = 10000, warmUp = 1000, reporter = {HtmlReporter.class})

public void textMinStringStream(){

list.stream().min(String::compareTo).get();

}

@JunitPerfConfig(duration = 10000, warmUp = 1000, reporter = {HtmlReporter.class})

public void testMinStringParallelStream(){

list.stream().parallel().min(String::compareTo).get();

}

private static ArrayList randomStringList(int listLength){

ArrayList list = new ArrayList<>(listLength);

Random rand = new Random();

int strLength = 10;

StringBuilder buf = new StringBuilder(strLength);

for(int i=0; i

buf.delete(0, buf.length());

for(int j=0; j

buf.append((char)('a'+ rand.nextInt(26)));

}

list.add(buf.toString());

}

return list;

}

}

测试用例三:

import com.github.houbb.junitperf.core.annotation.JunitPerfConfig;

import com.github.houbb.junitperf.core.report.impl.HtmlReporter;

import org.junit.jupiter.api.BeforeAll;

import java.util.*;

import java.util.stream.Collectors;

public class StreamObjectTest {

public static List orders;

@BeforeAll

public static void init() {

orders = Order.genOrders(10);

}

@JunitPerfConfig(duration = 10000, warmUp = 1000, reporter = {HtmlReporter.class})

public void testSumOrderForLoop(){

Map map = new HashMap<>();

for(Order od : orders){

String userName = od.getUserName();

Double v;

if((v=map.get(userName)) != null){

map.put(userName, v+od.getPrice());

}else{

map.put(userName, od.getPrice());

}

}

}

@JunitPerfConfig(duration = 10000, warmUp = 1000, reporter = {HtmlReporter.class})

public void testSumOrderStream(){

orders.stream().collect(

Collectors.groupingBy(Order::getUserName,

Collectors.summingDouble(Order::getPrice)));

}

@JunitPerfConfig(duration = 10000, warmUp = 1000, reporter = {HtmlReporter.class})

public void testSumOrderParallelStream(){

orders.parallelStream().collect(

Collectors.groupingBy(Order::getUserName,

Collectors.summingDouble(Order::getPrice)));

}

}

class Order{

private String userName;

private double price;

private long timestamp;

public Order(String userName, double price, long timestamp) {

this.userName = userName;

this.price = price;

this.timestamp = timestamp;

}

public String getUserName() {

return userName;

}

public double getPrice() {

return price;

}

public long getTimestamp() {

return timestamp;

}

public static List genOrders(int listLength){

ArrayList list = new ArrayList<>(listLength);

Random rand = new Random();

int users = listLength/200;// 200 orders per user

users = users==0 ? listLength : users;

ArrayList userNames = new ArrayList<>(users);

for(int i=0; i

userNames.add(UUID.randomUUID().toString());

}

for(int i=0; i

double price = rand.nextInt(1000);

String userName = userNames.get(rand.nextInt(users));

list.add(new Order(userName, price, System.nanoTime()));

}

return list;

}

@Override

public String toString(){

return userName + "::" + price;

}

}

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

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

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

小讯
上一篇 2025-03-13 10:58
下一篇 2025-03-02 16:42

相关推荐

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/53494.html