0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

list.sort()排序比stream().sorted()排序性能更好吗?

jf_ro2CN3Fa 来源:稀土掘金 2023-08-09 10:27 次阅读

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

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

但没说到为什么。

d679a17a-3654-11ee-9e74-dac502259ad0.jpg

有朋友也提到了这一点。

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

真的更好吗?

先简单写个 demo。

ListuserList=newArrayList<>();
Randomrand=newRandom();
for(inti=0;i< 10000 ; i++) {
        userList.add(rand.nextInt(1000));
    }
    ListuserList2=newArrayList<>();
userList2.addAll(userList);

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

LongstartTime=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。

ListuserList=newArrayList<>();
Randomrand=newRandom();
for(inti=0;i< 10000 ; i++) {
    userList.add(rand.nextInt(1000));
}
ListuserList2=newArrayList<>();
userList2.addAll(userList);

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

LongstartTime1=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 编译和内联优化。

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

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

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

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

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

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

importjava.util.*;
importjava.util.concurrent.ThreadLocalRandom;
importjava.util.concurrent.TimeUnit;
importjava.util.stream.Collectors;

@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.MICROSECONDS)
@Warmup(iterations=2,time=1)
@Measurement(iterations=5,time=5)
@Fork(1)
@State(Scope.Thread)
publicclassSortBenchmark{
@Param(value={"100","10000","100000"})
privateintoperationSize;
privatestaticListarrayList;
publicstaticvoidmain(String[]args)throwsRunnerException{
//启动基准测试
Optionsopt=newOptionsBuilder()
.include(SortBenchmark.class.getSimpleName())
.result("SortBenchmark.json")
.mode(Mode.All)
.resultFormat(ResultFormatType.JSON)
.build();
newRunner(opt).run();
}
@Setup
publicvoidinit(){
arrayList=newArrayList<>();
Randomrandom=newRandom();
for(inti=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
publicvoidstreamSorted(Blackholeblackhole){
arrayList=arrayList.stream().sorted(Comparator.comparing(e->e)).collect(Collectors.toList());
blackhole.consume(arrayList);
}
}

性能测试结果:

d6919d0c-3654-11ee-9e74-dac502259ad0.jpg

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

为什么更好?

流本身的损耗

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

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

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

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

importjava.util.ArrayList;
importjava.util.Comparator;
importjava.util.List;
importjava.util.Random;
importjava.util.concurrent.TimeUnit;
importjava.util.stream.Collectors;

@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.MICROSECONDS)
@Warmup(iterations=2,time=1)
@Measurement(iterations=5,time=5)
@Fork(1)
@State(Scope.Thread)
publicclassSortBenchmark3{
@Param(value={"100","10000"})
privateintoperationSize;//操作次数
privatestaticListarrayList;
publicstaticvoidmain(String[]args)throwsRunnerException{
//启动基准测试
Optionsopt=newOptionsBuilder()
.include(SortBenchmark3.class.getSimpleName())//要导入的测试类
.result("SortBenchmark3.json")
.mode(Mode.All)
.resultFormat(ResultFormatType.JSON)
.build();
newRunner(opt).run();//执行测试
}

@Setup
publicvoidinit(){
//启动执行事件
arrayList=newArrayList<>();
Randomrandom=newRandom();
for(inti=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 测试将一个集合转为流再排序再收集回来的全过程耗时。

测试结果如下:

d6a99132-3654-11ee-9e74-dac502259ad0.jpg

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

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

排序过程

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

d6bc1794-3654-11ee-9e74-dac502259ad0.jpg

1 begin方法初始化一个数组。

2 accept 接收上游数据。

3 end 方法开始进行排序。

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

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

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

这一步我就不做了。

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

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

补充说明:

本文说的 stream() 流指的是串行流,而不是并行流。

绝大多数场景下,几百几千几万的数据,开心就好,怎么方便怎么用,没有必要去计较这点性能差异。





审核编辑:刘清

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • SQL
    SQL
    +关注

    关注

    1

    文章

    738

    浏览量

    43466
  • JAVA语言
    +关注

    关注

    0

    文章

    138

    浏览量

    19944
  • JVM
    JVM
    +关注

    关注

    0

    文章

    152

    浏览量

    12129

原文标题:为什么list.sort()比Stream().sorted()更快?

文章出处:【微信号:芋道源码,微信公众号:芋道源码】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    FPGA排序-冒泡排序介绍

    排序算法是图像处理中经常使用一种算法,常见的排序算法有插入排序、希尔排序、选择排序、冒泡排序、归
    发表于 07-17 10:12 675次阅读
    FPGA<b class='flag-5'>排序</b>-冒泡<b class='flag-5'>排序</b>介绍

    排序算法之选择排序

    选择排序: (Selection sort)是一种简单直观的排序算法,也是一种不稳定的排序方法。 选择排序的原理: 一组无序待排数组,做升序
    的头像 发表于 09-25 16:30 845次阅读
    <b class='flag-5'>排序</b>算法之选择<b class='flag-5'>排序</b>

    排序与索引

    记录顺序。排序后生成一个新表,新表的记录按新的物理顺序排列。 命令格式:SORT TO <新文件名> ON <字段名1> [/A|/D] [/C
    发表于 03-10 15:58

    VHDL中的排序算法怎么实现?

    be able to read the numbers from the BRAM, sort them and write the sorted "list" in another
    发表于 03-29 13:44

    冒泡排序法三部曲の冒泡排序原理版(一)

    [table][tr][td]声明:编译环境为VS2017 语言:C language针对对象:对n个数从小到大进行排序(从大到小同理)思路分析:经典的bubble sort(冒泡排序)原理类似于
    发表于 09-12 10:42

    PHP数组排序

    数组排序(6个) sort() - 以升序对数组排序rsort() - 以降序对数组排序 reversal sort)asort() - 根
    发表于 11-04 07:48

    c语言排序算法之选择排序

    法就是"先选后排"。假定待排序数字序列均为整数,且共有NUM个,大小随机排列,存放在list[NUM]中。        首先假定list[0]为序列中最小的数字,再依次拿它与list
    发表于 11-16 10:25 3321次阅读
    c语言<b class='flag-5'>排序</b>算法之选择<b class='flag-5'>排序</b>法

    C语言中的排序算法了解

    选择排序(Selection sort)是一种简单直观的排序算法。它的工作原理如下。首先在未排序序列中找到最小(大)元素,存放到排序序列的起
    的头像 发表于 11-12 14:52 2486次阅读

    Linux系统中sort排序命令的使用教程

    sort命令的功能是对文件中的各行进行排序sort命令有许多非常实用的选项,这些选项最初是用来对数据库格式的文件内容进行各种排序操作的。实际上,
    发表于 04-02 14:33 327次阅读

    Python中的排序

    另外一种排序方法是 sorted ,此方法不是原地排序,以第一个值进行排序,同样也是默认升序排序
    的头像 发表于 09-07 16:25 1958次阅读
    Python中的<b class='flag-5'>排序</b>

    排序算法merge-sort的基础知识

    本文介绍、解释、评估和实现了排序算法merge-sort 。本文的目的是为您提供有关合并排序算法的可靠背景信息,该算法是更复杂算法的基础知识。
    的头像 发表于 04-07 17:54 2221次阅读
    <b class='flag-5'>排序</b>算法merge-<b class='flag-5'>sort</b>的基础知识

    冒泡排序的基本思想

    冒泡排序的英文Bubble Sort,是一种最基础的交换排序。之所以叫做冒泡排序,因为每一个元素都可以像小气泡一样,根据自身大小一点一点向数组的一侧移动。
    的头像 发表于 01-20 11:38 5231次阅读
    冒泡<b class='flag-5'>排序</b>的基本思想

    使用C++ sort函数对vector进行自定义排序

    今天在学一些C++ STL容器,看到sort函数允许自定义排序规则,小小地实操了一下。
    的头像 发表于 07-22 10:12 1058次阅读

    排序算法有哪些

    1. 归并排序(递归版) 归并排序(MERGE-SORT)是利用归并的思想实现的排序方法,该算法采用经典的分治策略,即分为两步:分与治。 分:先递归分解数组成子数组 治:将分阶段得到的
    的头像 发表于 10-11 15:49 326次阅读
    <b class='flag-5'>排序</b>算法有哪些

    sort函数python用法

    sort()函数是Python中的内置函数之一,用于对可迭代对象进行排序。可迭代对象包括列表、元组和字符串等。sort()函数是一个灵活而强大的函数,在数据分析、算法实现等方面有着广泛
    的头像 发表于 11-21 15:15 697次阅读