0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

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

jf_ro2CN3Fa ? 來源:稀土掘金 ? 2023-08-09 10:27 ? 次閱讀

看到一個(gè)評論,里面提到了list.sort()和list.strem().sorted()排序的差異。

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

但沒說到為什么。

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

有朋友也提到了這一點(diǎn)。

本文重新開始,先問是不是,再問為什么。

真的更好嗎?

先簡單寫個(gè) demo。

ListuserList=newArrayList<>();
Randomrand=newRandom();
for(inti=0;iuserList2=newArrayList<>();
userList2.addAll(userList);

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

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

輸出

stream.sort耗時(shí):62ms
List.sort()耗時(shí):7ms

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

能證明嗎?

不一定吧。

再把 demo 變換一下,先輸出stream.sort。

ListuserList=newArrayList<>();
Randomrand=newRandom();
for(inti=0;iuserList2=newArrayList<>();
userList2.addAll(userList);

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

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

此時(shí)輸出變成了。

List.sort()耗時(shí):68ms
stream.sort耗時(shí):13ms

這能證明上面的結(jié)論錯誤了嗎?

都不能。

兩種方式都不能證明到底誰更快。

使用這種方式在很多場景下是不夠的,某些場景下,JVM 會對代碼進(jìn)行 JIT 編譯和內(nèi)聯(lián)優(yōu)化。

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

此時(shí),代碼優(yōu)化前后執(zhí)行的結(jié)果就會非常大。

基準(zhǔn)測試是指通過設(shè)計(jì)科學(xué)的測試方法、測試工具和測試系統(tǒng),實(shí)現(xiàn)對一類測試對象的某項(xiàng)性能指標(biāo)進(jìn)行定量的和可對比的測試。

基準(zhǔn)測試使得被測試代碼獲得足夠預(yù)熱,讓被測試代碼得到充分的 JIT 編譯和優(yōu)化。

下面是通過 JMH 做一下基準(zhǔn)測試,分別測試集合大小在 100,10000,100000 時(shí)兩種排序方式的性能差異。

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{
//啟動基準(zhǔn)測試
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;ie));
blackhole.consume(arrayList);
}
@Benchmark
publicvoidstreamSorted(Blackholeblackhole){
arrayList=arrayList.stream().sorted(Comparator.comparing(e->e)).collect(Collectors.toList());
blackhole.consume(arrayList);
}
}

性能測試結(jié)果:

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

可以看到,list.sort()效率確實(shí)比stream().sorted()要好。

為什么更好?

流本身的損耗

java 的 stream 讓我們可以在應(yīng)用層就可以高效地實(shí)現(xiàn)類似數(shù)據(jù)庫 SQL 的聚合操作了,它可以讓代碼更加簡潔優(yōu)雅。

但是,假設(shè)我們要對一個(gè) list 排序,得先把 list 轉(zhuǎn)成 stream 流,排序完成后需要將數(shù)據(jù)收集起來重新形成 list,這部份額外的開銷有多大呢?

我們可以通過以下代碼來進(jìn)行基準(zhǔn)測試。

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;//操作次數(shù)
privatestaticListarrayList;
publicstaticvoidmain(String[]args)throwsRunnerException{
//啟動基準(zhǔn)測試
Optionsopt=newOptionsBuilder()
.include(SortBenchmark3.class.getSimpleName())//要導(dǎo)入的測試類
.result("SortBenchmark3.json")
.mode(Mode.All)
.resultFormat(ResultFormatType.JSON)
.build();
newRunner(opt).run();//執(zhí)行測試
}

@Setup
publicvoidinit(){
//啟動執(zhí)行事件
arrayList=newArrayList<>();
Randomrandom=newRandom();
for(inti=0;i

方法 stream 測試將一個(gè)集合轉(zhuǎn)為流再收集回來的耗時(shí)。

方法 sort 測試將一個(gè)集合轉(zhuǎn)為流再排序再收集回來的全過程耗時(shí)。

測試結(jié)果如下:

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

可以發(fā)現(xiàn),集合轉(zhuǎn)為流再收集回來的過程,肯定會耗時(shí),但是它占全過程的比率并不算高。

因此,這部只能說是小部份的原因。

排序過程

我們可以通過以下源碼很直觀的看到。

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

1 begin方法初始化一個(gè)數(shù)組。

2 accept 接收上游數(shù)據(jù)。

3 end 方法開始進(jìn)行排序。

這里第 3 步直接調(diào)用了原生的排序方法,完成排序后,第 4 步,遍歷向下游發(fā)送數(shù)據(jù)。

所以通過源碼,我們也能很明顯地看到,stream()排序所需時(shí)間肯定是 > 原生排序時(shí)間。

只不過,這里要量化地搞明白,到底多出了多少,這里得去編譯 jdk 源碼,在第 3 步前后將時(shí)間打印出來。

這一步我就不做了。

感興趣的朋友可以去測一下。

不過我覺得這兩點(diǎn)也能很好地回答,為什么list.sort()比Stream().sorted()更快。

補(bǔ)充說明:

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

絕大多數(shù)場景下,幾百幾千幾萬的數(shù)據(jù),開心就好,怎么方便怎么用,沒有必要去計(jì)較這點(diǎn)性能差異。





審核編輯:劉清

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報(bào)投訴
  • SQL
    SQL
    +關(guān)注

    關(guān)注

    1

    文章

    750

    瀏覽量

    43900
  • JAVA語言
    +關(guān)注

    關(guān)注

    0

    文章

    138

    瀏覽量

    20027
  • JVM
    JVM
    +關(guān)注

    關(guān)注

    0

    文章

    155

    瀏覽量

    12168

原文標(biāo)題:為什么list.sort()比Stream().sorted()更快?

文章出處:【微信號:芋道源碼,微信公眾號:芋道源碼】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。

收藏 人收藏

    評論

    相關(guān)推薦

    FPGA排序-冒泡排序介紹

    排序算法是圖像處理中經(jīng)常使用一種算法,常見的排序算法有插入排序、希爾排序、選擇排序、冒泡排序、歸
    發(fā)表于 07-17 10:12 ?963次閱讀
    FPGA<b class='flag-5'>排序</b>-冒泡<b class='flag-5'>排序</b>介紹

    排序算法之選擇排序

    選擇排序: (Selection sort)是一種簡單直觀的排序算法,也是一種不穩(wěn)定的排序方法。 選擇排序的原理: 一組無序待排數(shù)組,做升序
    的頭像 發(fā)表于 09-25 16:30 ?1333次閱讀
    <b class='flag-5'>排序</b>算法之選擇<b class='flag-5'>排序</b>

    排序與索引

    記錄順序。排序后生成一個(gè)新表,新表的記錄按新的物理順序排列。 命令格式:SORT TO <新文件名> ON <字段名1> [/A|/D] [/C
    發(fā)表于 03-10 15:58

    VHDL中的排序算法怎么實(shí)現(xiàn)?

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

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

    的bubble sort(冒泡排序)原理類似于氣泡上升過程,到自身的密度小于上一層介質(zhì)則上升,排序同理。以數(shù)組{5,4,3,2,1}為例: 第一輪:由于5大于4,則5右移一位,5大于3,則繼續(xù)右移,5>2
    發(fā)表于 09-12 10:30

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

    [table][tr][td]聲明:編譯環(huán)境為VS2017 語言:C language針對對象:對n個(gè)數(shù)從小到大進(jìn)行排序(從大到小同理)思路分析:經(jīng)典的bubble sort(冒泡排序)原理類似于
    發(fā)表于 09-12 10:42

    PHP數(shù)組排序

    數(shù)組排序(6個(gè)) sort() - 以升序?qū)?shù)組排序rsort() - 以降序?qū)?shù)組排序 reversal sort)asort() - 根
    發(fā)表于 11-04 07:48

    c語言排序算法之選擇排序

    法就是"先選后排"。假定待排序數(shù)字序列均為整數(shù),且共有NUM個(gè),大小隨機(jī)排列,存放在list[NUM]中。 ? ? ? ?首先假定list[0]為序列中最小的數(shù)字,再依次拿它與list
    發(fā)表于 11-16 10:25 ?3397次閱讀
    c語言<b class='flag-5'>排序</b>算法之選擇<b class='flag-5'>排序</b>法

    C語言中的排序算法了解

    選擇排序(Selection sort)是一種簡單直觀的排序算法。它的工作原理如下。首先在未排序序列中找到最小(大)元素,存放到排序序列的起
    的頭像 發(fā)表于 11-12 14:52 ?2570次閱讀

    Linux系統(tǒng)中sort排序命令的使用教程

    sort命令的功能是對文件中的各行進(jìn)行排序。sort命令有許多非常實(shí)用的選項(xiàng),這些選項(xiàng)最初是用來對數(shù)據(jù)庫格式的文件內(nèi)容進(jìn)行各種排序操作的。實(shí)際上,
    發(fā)表于 04-02 14:33 ?378次閱讀

    Python中的排序

    另外一種排序方法是 sorted ,此方法不是原地排序,以第一個(gè)值進(jìn)行排序,同樣也是默認(rèn)升序排序
    的頭像 發(fā)表于 09-07 16:25 ?2094次閱讀
    Python中的<b class='flag-5'>排序</b>

    排序算法merge-sort的基礎(chǔ)知識

    本文介紹、解釋、評估和實(shí)現(xiàn)了排序算法merge-sort 。本文的目的是為您提供有關(guān)合并排序算法的可靠背景信息,該算法是更復(fù)雜算法的基礎(chǔ)知識。
    的頭像 發(fā)表于 04-07 17:54 ?2446次閱讀
    <b class='flag-5'>排序</b>算法merge-<b class='flag-5'>sort</b>的基礎(chǔ)知識

    冒泡排序的基本思想

    冒泡排序的英文Bubble Sort,是一種最基礎(chǔ)的交換排序。之所以叫做冒泡排序,因?yàn)槊恳粋€(gè)元素都可以像小氣泡一樣,根據(jù)自身大小一點(diǎn)一點(diǎn)向數(shù)組的一側(cè)移動。
    的頭像 發(fā)表于 01-20 11:38 ?5648次閱讀
    冒泡<b class='flag-5'>排序</b>的基本思想

    使用C++ sort函數(shù)對vector進(jìn)行自定義排序

    今天在學(xué)一些C++ STL容器,看到sort函數(shù)允許自定義排序規(guī)則,小小地實(shí)操了一下。
    的頭像 發(fā)表于 07-22 10:12 ?1535次閱讀

    sort函數(shù)python用法

    sort()函數(shù)是Python中的內(nèi)置函數(shù)之一,用于對可迭代對象進(jìn)行排序??傻鷮ο蟀斜?、元組和字符串等。sort()函數(shù)是一個(gè)靈活而強(qiáng)大的函數(shù),在數(shù)據(jù)分析、算法實(shí)現(xiàn)等方面有著廣泛
    的頭像 發(fā)表于 11-21 15:15 ?1046次閱讀