Redian新闻
>
为什么list.sort()比Stream().sorted()更快?

为什么list.sort()比Stream().sorted()更快?

公众号新闻

👉 这是一个或许对你有用的社群

🐱 一对一交流/面试小册/简历优化/求职解惑,欢迎加入芋道快速开发平台知识星球。下面是星球提供的部分资料: 

👉这是一个或许对你有用的开源项目

国产 Star 破 10w+ 的开源项目,前端包括管理后台 + 微信小程序,后端支持单体和微服务架构。

功能涵盖 RBAC 权限、SaaS 多租户、数据权限、商城、支付、工作流、大屏报表、微信公众号等等功能:

  • Boot 地址:https://gitee.com/zhijiantianya/ruoyi-vue-pro
  • Cloud 地址:https://gitee.com/zhijiantianya/yudao-cloud
  • 视频教程:https://doc.iocoder.cn

来源:juejin.cn/post/
7262274383287500860


看到一个评论,里面提到了list.sort()list.strem().sorted()排序的差异。

说到list.sort()排序比stream().sorted()排序性能更好。

但没说到为什么。

有朋友也提到了这一点。

本文重新开始,先问是不是,再问为什么。

真的更好吗?

先简单写个 demo。

List<Integer> userList = new ArrayList<>();
    Random rand = new Random();
    for (int i = 0; i < 10000 ; i++) {
        userList.add(rand.nextInt(1000));
    }
    List<Integer> userList2 = new ArrayList<>();
    userList2.addAll(userList);

    Long startTime1 = System.currentTimeMillis();
    userList2.stream().sorted(Comparator.comparing(Integer::intValue)).collect(Collectors.toList());
    System.out.println("stream.sort耗时:"+(System.currentTimeMillis() - startTime1)+"ms");

    Long startTime = System.currentTimeMillis();
    userList.sort(Comparator.comparing(Integer::intValue));
    System.out.println("List.sort()耗时:"+(System.currentTimeMillis()-startTime)+"ms");

输出

stream.sort耗时:62ms
List.sort()耗时:7ms

由此可见 list 原生排序性能更好。

能证明吗?

不一定吧。

再把 demo 变换一下,先输出stream.sort

List<Integer> userList = new ArrayList<>();
Random rand = new Random();
for (int i = 0; i < 10000 ; i++) {
    userList.add(rand.nextInt(1000));
}
List<Integer> userList2 = new ArrayList<>();
userList2.addAll(userList);

Long startTime = System.currentTimeMillis();
userList.sort(Comparator.comparing(Integer::intValue));
System.out.println("List.sort()耗时:"+(System.currentTimeMillis()-startTime)+"ms");

Long startTime1 = System.currentTimeMillis();
userList2.stream().sorted(Comparator.comparing(Integer::intValue)).collect(Collectors.toList());
System.out.println("stream.sort耗时:"+(System.currentTimeMillis() - startTime1)+"ms");

此时输出变成了。

List.sort()耗时:68ms
stream.sort耗时:13ms

这能证明上面的结论错误了吗?

都不能。

两种方式都不能证明到底谁更快。

使用这种方式在很多场景下是不够的,某些场景下,JVM 会对代码进行 JIT 编译和内联优化。

Long startTime = System.currentTimeMillis();
...
System.currentTimeMillis() - startTime

此时,代码优化前后执行的结果就会非常大。

基准测试是指通过设计科学的测试方法、测试工具和测试系统,实现对一类测试对象的某项性能指标进行定量的和可对比的测试。

基准测试使得被测试代码获得足够预热,让被测试代码得到充分的 JIT 编译和优化。

下面是通过 JMH 做一下基准测试,分别测试集合大小在 100,10000,100000 时两种排序方式的性能差异。

import org.openjdk.jmh.annotations.*;
import org.openjdk.jmh.infra.Blackhole;
import org.openjdk.jmh.results.format.ResultFormatType;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.RunnerException;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;

import java.util.*;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.MICROSECONDS)
@Warmup(iterations = 2, time = 1)
@Measurement(iterations = 5, time = 5)
@Fork(1)
@State(Scope.Thread)
public class SortBenchmark {
    @Param(value = {"100""10000""100000"})
    private int operationSize; 
    private static List<Integer> arrayList;
    public static void main(String[] args) throws RunnerException {
        // 启动基准测试
        Options opt = new OptionsBuilder()
            .include(SortBenchmark.class.getSimpleName()) 
            .result("SortBenchmark.json")
            .mode(Mode.All)
            .resultFormat(ResultFormatType.JSON)
            .build()
;
        new Runner(opt).run(); 
    }
    @Setup
    public void init() {
        arrayList = new ArrayList<>();
        Random random = new Random();
        for (int i = 0; i < operationSize; i++) {
            arrayList.add(random.nextInt(10000));
        }
    }
    @Benchmark
    public void sort(Blackhole blackhole) {
        arrayList.sort(Comparator.comparing(e -> e));
        blackhole.consume(arrayList);
    }
    @Benchmark
    public void streamSorted(Blackhole blackhole) {
        arrayList = arrayList.stream().sorted(Comparator.comparing(e -> e)).collect(Collectors.toList());
        blackhole.consume(arrayList);
    }
}

性能测试结果:

可以看到,list.sort()效率确实比stream().sorted()要好。

为什么更好?

基于 Spring Boot + MyBatis Plus + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能

  • 项目地址:https://github.com/YunaiV/ruoyi-vue-pro
  • 视频教程:https://doc.iocoder.cn/video/

流本身的损耗

java 的 stream 让我们可以在应用层就可以高效地实现类似数据库 SQL 的聚合操作了,它可以让代码更加简洁优雅。

但是,假设我们要对一个 list 排序,得先把 list 转成 stream 流,排序完成后需要将数据收集起来重新形成 list,这部份额外的开销有多大呢?

我们可以通过以下代码来进行基准测试。

import org.openjdk.jmh.annotations.*;
import org.openjdk.jmh.infra.Blackhole;
import org.openjdk.jmh.results.format.ResultFormatType;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.RunnerException;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Random;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.MICROSECONDS)
@Warmup(iterations = 2, time = 1)
@Measurement(iterations = 5, time = 5)
@Fork(1)
@State(Scope.Thread)
public class SortBenchmark3 {
    @Param(value = {"100""10000"})
    private int operationSize; // 操作次数
    private static List<Integer> arrayList;
    public static void main(String[] args) throws RunnerException {
        // 启动基准测试
        Options opt = new OptionsBuilder()
            .include(SortBenchmark3.class.getSimpleName()) // 要导入的测试类
            .result("SortBenchmark3.json")
            .mode(Mode.All)
            .resultFormat(ResultFormatType.JSON)
            .build()
;
        new Runner(opt).run(); // 执行测试
    }

    @Setup
    public void init() {
        // 启动执行事件
        arrayList = new ArrayList<>();
        Random random = new Random();
        for (int i = 0; i < operationSize; i++) {
            arrayList.add(random.nextInt(10000));
        }
    }

    @Benchmark
    public void stream(Blackhole blackhole) {
        arrayList.stream().collect(Collectors.toList());
        blackhole.consume(arrayList);
    }

    @Benchmark
    public void sort(Blackhole blackhole) {
        arrayList.stream().sorted(Comparator.comparing(Integer::intValue)).collect(Collectors.toList());
        blackhole.consume(arrayList);
    }
}

方法 stream 测试将一个集合转为流再收集回来的耗时。

方法 sort 测试将一个集合转为流再排序再收集回来的全过程耗时。

测试结果如下:

可以发现,集合转为流再收集回来的过程,肯定会耗时,但是它占全过程的比率并不算高。

因此,这部只能说是小部份的原因。

基于 Spring Cloud Alibaba + Gateway + Nacos + RocketMQ + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能

  • 项目地址:https://github.com/YunaiV/yudao-cloud
  • 视频教程:https://doc.iocoder.cn/video/

排序过程

我们可以通过以下源码很直观的看到。

  • 1 begin方法初始化一个数组。
  • 2 accept 接收上游数据。
  • 3 end 方法开始进行排序。

这里第 3 步直接调用了原生的排序方法,完成排序后,第 4 步,遍历向下游发送数据。

所以通过源码,我们也能很明显地看到,stream()排序所需时间肯定是 > 原生排序时间。

只不过,这里要量化地搞明白,到底多出了多少,这里得去编译 jdk 源码,在第 3 步前后将时间打印出来。

这一步我就不做了。

感兴趣的朋友可以去测一下。

不过我觉得这两点也能很好地回答,为什么list.sort()Stream().sorted()更快。

补充说明:

  1. 本文说的 stream() 流指的是串行流,而不是并行流。
  2. 绝大多数场景下,几百几千几万的数据,开心就好,怎么方便怎么用,没有必要去计较这点性能差异。

欢迎加入我的知识星球,全面提升技术能力。

👉 加入方式,长按”或“扫描”下方二维码噢

星球的内容包括:项目实战、面试招聘、源码解析、学习路线。

文章有帮助的话,在看,转发吧。

谢谢支持哟 (*^__^*)

微信扫码关注该文公众号作者

戳这里提交新闻线索和高质量文章给我们。
相关阅读
为什么Lisa参加疯马秀,会被骂到现在这个地步?Troubled Singing Reality Show Accused of Mistreating Coco Lee#英语学习#Restrain, Refrain, and Restrict有什么区别?看看你能答对这道题吗?Redmi 新机再曝,Redmi Note 13、Redmi K70期待哪款?精选SDE岗位 | Red Hat Software、Blackstone、Aristocrat公司岗位发布!Extreme Boot Camps for Kids Are Multiplying. So Are Accidents.Redmi首款曲面屏来了!Redmi Note 13 系列定档9月21日发布华为提出Sorted LLaMA:SoFT代替SFT,训练多合一大语言模型Ex-Soldier Sleeps in Cave for 22 Years to Protect Song TreasuresRescue Teams Struggle to Reach Isolated Areas in Flood-Hit Hebei𝐂𝐚𝐥𝐧𝐢𝐊𝐞𝐚𝐧双皮奶内衣裤,软弹有度,上身0束缚~不改革可能会死,但改革死得更快?Extreme Drinking Claims Another Chinese LivestreamerChinese Children Trapped in Myanmar Scam Centers: ReportNo Pre-entry COVID Test Required首个Unified Redis Release,Redis影响最深远的版本发布Established Chinese Apparel Brands Struggle to Stay AfloatCubist Systematic Strategies岗位专辑 | 海量岗位来袭!原创《陈寅恪学问之“不古不今”》 其三冷明和他的书《为了你走遍草原》部分旧版Chase Freedom用户被强制“暖心升级”成Chase Freedom UnlimitedHundreds of Chinese Arrested For Cyberscams in Laos, MyanmarPNAS | 男人的存在,会让女人老得更快?研究发现:与雄鼠一起生活,雌鼠连寿命都会变短TruEdu 秋季写作课程推荐 - Advanced Writers Class老钱:谁被禁止购买美国房地产?减脂如何更快?一招干掉全身脂肪,腰围减掉10CM!【𝐂𝐚𝐥𝐧𝐢𝐊𝐞𝐚𝐧双皮奶无痕内裤】49元三条!巨巨巨好穿 !!简直就是辣妹顶配,食品级冰箱收纳盒【一日团】为什么Longchamp可以背一辈子?真正懂包的人都是这样…市区River North房源RN01 | 不收中介费/Streeterville & River North 区域精选SDE岗位 | Tesla、Apple、Red Hat Software公司岗位发布!谁是草原土匪 冷明你好,我是筚(bì)篥( lì)!ICCV 2023 | 更快更强!北理工&旷视提出StreamPETR:纯视觉感知与激光雷达终有一战之力!克利奥帕特拉:野心勃勃又媚惑非凡的埃及艳后Redmi K60 Pro或已停产,Redmi K60 Ultra蓄势待发
logo
联系我们隐私协议©2024 redian.news
Redian新闻
Redian.news刊载任何文章,不代表同意其说法或描述,仅为提供更多信息,也不构成任何建议。文章信息的合法性及真实性由其作者负责,与Redian.news及其运营公司无关。欢迎投稿,如发现稿件侵权,或作者不愿在本网发表文章,请版权拥有者通知本网处理。