<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

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

          共 11777字,需瀏覽 24分鐘

           ·

          2023-08-16 14:55


          胖虎和朋友原創(chuàng)的視頻教程有興趣的可以看看


          (文末附課程大綱)


          ??2023 最新,Java成神之路,架構(gòu)視頻(點(diǎn)擊查看)


          ??超全技術(shù)棧的Java入門+進(jìn)階+實(shí)戰(zhàn)!(點(diǎn)擊查看)


          來源:juejin.cn/post/7262274383287500860
          • 真的更好嗎?
          • 流本身的損耗
          • 排序過程

          看到一個(gè)評(píng)論,里面提到了list.sort()list.strem().sorted()排序的差異。
          說到list.sort()排序比stream().sorted()排序性能更好。
          但沒說到為什么。
          有朋友也提到了這一點(diǎn)。
          本文重新開始,先問是不是,再問為什么。

          真的更好嗎?

          先簡(jiǎn)單寫個(gè) 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耗時(shí):"+(System.currentTimeMillis() - startTime1)+"ms");

              Long startTime = 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
             
          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()耗時(shí):"+(System.currentTimeMillis()-startTime)+"ms");

          Long startTime1 = 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é)論錯(cuò)誤了嗎?
          都不能。
          兩種方式都不能證明到底誰更快。
          使用這種方式在很多場(chǎng)景下是不夠的,某些場(chǎng)景下,JVM 會(huì)對(duì)代碼進(jìn)行 JIT 編譯和內(nèi)聯(lián)優(yōu)化。
             
          Long startTime = System.currentTimeMillis();
          ...
          System.currentTimeMillis() - startTime
          此時(shí),代碼優(yōu)化前后執(zhí)行的結(jié)果就會(huì)非常大。
          ?
          基準(zhǔn)測(cè)試是指通過設(shè)計(jì)科學(xué)的測(cè)試方法、測(cè)試工具和測(cè)試系統(tǒng),實(shí)現(xiàn)對(duì)一類測(cè)試對(duì)象的某項(xiàng)性能指標(biāo)進(jìn)行定量的和可對(duì)比的測(cè)試。
          ?
          基準(zhǔn)測(cè)試使得被測(cè)試代碼獲得足夠預(yù)熱,讓被測(cè)試代碼得到充分的 JIT 編譯和優(yōu)化。
          下面是通過 JMH 做一下基準(zhǔn)測(cè)試,分別測(cè)試集合大小在 100,10000,100000 時(shí)兩種排序方式的性能差異。
             
          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 {
                  // 啟動(dòng)基準(zhǔn)測(cè)試
                  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);
              }
          }
          性能測(cè)試結(jié)果:
          可以看到,list.sort()效率確實(shí)比stream().sorted()要好。
          為什么更好?

          流本身的損耗

          java 的 stream 讓我們可以在應(yīng)用層就可以高效地實(shí)現(xiàn)類似數(shù)據(jù)庫(kù) SQL 的聚合操作了,它可以讓代碼更加簡(jiǎn)潔優(yōu)雅。
          但是,假設(shè)我們要對(duì)一個(gè) list 排序,得先把 list 轉(zhuǎn)成 stream 流,排序完成后需要將數(shù)據(jù)收集起來重新形成 list,這部份額外的開銷有多大呢?
          我們可以通過以下代碼來進(jìn)行基準(zhǔn)測(cè)試。
             
          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; // 操作次數(shù)
              private static List<Integer> arrayList;
              public static void main(String[] args) throws RunnerException {
                  // 啟動(dòng)基準(zhǔn)測(cè)試
                  Options opt = new OptionsBuilder()
                      .include(SortBenchmark3.class.getSimpleName()) // 要導(dǎo)入的測(cè)試類
                      .result("SortBenchmark3.json")
                      .mode(Mode.All)
                      .resultFormat(ResultFormatType.JSON)
                      .build()
          ;
                  new Runner(opt).run(); // 執(zhí)行測(cè)試
              }

              @Setup
              public void init() {
                  // 啟動(dòng)執(zhí)行事件
                  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 測(cè)試將一個(gè)集合轉(zhuǎn)為流再收集回來的耗時(shí)。
          方法 sort 測(cè)試將一個(gè)集合轉(zhuǎn)為流再排序再收集回來的全過程耗時(shí)。
          測(cè)試結(jié)果如下:
          可以發(fā)現(xiàn),集合轉(zhuǎn)為流再收集回來的過程,肯定會(huì)耗時(shí),但是它占全過程的比率并不算高。
          因此,這部只能說是小部份的原因。

          排序過程

          我們可以通過以下源碼很直觀的看到。
          • 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í)間打印出來。
          這一步我就不做了。
          感興趣的朋友可以去測(cè)一下。
          不過我覺得這兩點(diǎn)也能很好地回答,為什么list.sort()Stream().sorted()更快。
          補(bǔ)充說明:
          1. 本文說的 stream() 流指的是串行流,而不是并行流。
          2. 絕大多數(shù)場(chǎng)景下,幾百幾千幾萬的數(shù)據(jù),開心就好,怎么方便怎么用,沒有必要去計(jì)較這點(diǎn)性能差異。
             

               

          胖虎聯(lián)合兩位大佬朋友,一位是知名培訓(xùn)機(jī)構(gòu)講師和科大訊飛架構(gòu),聯(lián)合打造了《Java架構(gòu)師成長(zhǎng)之路》的視頻教程。完全對(duì)標(biāo)外面2萬左右的培訓(xùn)課程。

          除了基本的視頻教程之外,還提供了超詳細(xì)的課堂筆記,以及源碼等資料包..


          課程階段:

          1. Java核心 提升閱讀源碼的內(nèi)功心法
          2. 深入講解企業(yè)開發(fā)必備技術(shù)棧,夯實(shí)基礎(chǔ),為跳槽加薪增加籌碼
          3. 分布式架構(gòu)設(shè)計(jì)方法論。為學(xué)習(xí)分布式微服務(wù)做鋪墊
          4. 學(xué)習(xí)NetFilx公司產(chǎn)品,如Eureka、Hystrix、Zuul、Feign、Ribbon等,以及學(xué)習(xí)Spring Cloud Alibabba體系
          5. 微服務(wù)架構(gòu)下的性能優(yōu)化
          6. 中間件源碼剖析
          7. 元原生以及虛擬化技術(shù)
          8. 從0開始,項(xiàng)目實(shí)戰(zhàn) SpringCloud Alibaba電商項(xiàng)目

          點(diǎn)擊下方超鏈接查看詳情(或者點(diǎn)擊文末閱讀原文):

          (點(diǎn)擊查看)  2023年,最新Java架構(gòu)師成長(zhǎng)之路 視頻教程!

          以下是課程大綱,大家可以雙擊打開原圖查看


          瀏覽 2711
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  久草香蕉视频 | 97国产精品视频人人做人人爱 | 国产夫妻手机在线 | 三级片亚洲无码 | 亚洲精品夜色 |