看到一個評論,里面提到了list.sort()和list.strem().sorted()排序的差異。
說到list.sort()排序比stream().sorted()排序性能更好。
但沒說到為什么。
有朋友也提到了這一點。
本文重新開始,先問是不是,再問為什么。
真的更好嗎?
先簡單寫個 demo。
ListuserList=newArrayList<>(); Randomrand=newRandom(); for(inti=0;i10000?;?i++)?{ ????????userList.add(rand.nextInt(1000)); ????} ????List userList2=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;i10000?;?i++)?{ ????userList.add(rand.nextInt(1000)); } List userList2=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;ie)); blackhole.consume(arrayList); } @Benchmark publicvoidstreamSorted(Blackholeblackhole){ arrayList=arrayList.stream().sorted(Comparator.comparing(e->e)).collect(Collectors.toList()); blackhole.consume(arrayList); } }
性能測試結果:
可以看到,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
方法 stream 測試將一個集合轉為流再收集回來的耗時。
方法 sort 測試將一個集合轉為流再排序再收集回來的全過程耗時。
測試結果如下:
可以發現,集合轉為流再收集回來的過程,肯定會耗時,但是它占全過程的比率并不算高。
因此,這部只能說是小部份的原因。
排序過程
我們可以通過以下源碼很直觀的看到。
1 begin方法初始化一個數組。
2 accept 接收上游數據。
3 end 方法開始進行排序。
這里第 3 步直接調用了原生的排序方法,完成排序后,第 4 步,遍歷向下游發送數據。
所以通過源碼,我們也能很明顯地看到,stream()排序所需時間肯定是 > 原生排序時間。
只不過,這里要量化地搞明白,到底多出了多少,這里得去編譯 jdk 源碼,在第 3 步前后將時間打印出來。
這一步我就不做了。
感興趣的朋友可以去測一下。
不過我覺得這兩點也能很好地回答,為什么list.sort()比Stream().sorted()更快。
補充說明:
本文說的 stream() 流指的是串行流,而不是并行流。
絕大多數場景下,幾百幾千幾萬的數據,開心就好,怎么方便怎么用,沒有必要去計較這點性能差異。
審核編輯:劉清
-
SQL
+關注
關注
1文章
780瀏覽量
44800 -
JAVA語言
+關注
關注
0文章
138瀏覽量
20509 -
JVM
+關注
關注
0文章
160瀏覽量
12516
原文標題:為什么list.sort()比Stream().sorted()更快?
文章出處:【微信號:芋道源碼,微信公眾號:芋道源碼】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
VHDL中的排序算法怎么實現?
冒泡排序法三部曲の冒泡排序原理版(一)
c語言排序算法之選擇排序法

評論