真的更好嗎?
先簡(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原生排序性能更好。
能證明嗎?
證據(jù)錯(cuò)了。
再把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()更快。文章來源:http://www.zghlxwxcb.cn/news/detail-629100.html
補(bǔ)充說明:文章來源地址http://www.zghlxwxcb.cn/news/detail-629100.html
- 本文說的stream()流指的是串行流,而不是并行流。
- 絕大多數(shù)場(chǎng)景下,幾百幾千幾萬(wàn)的數(shù)據(jù),開心就好,怎么方便怎么用,沒有必要去計(jì)較這點(diǎn)性能差異。
到了這里,關(guān)于為什么list.sort()比Stream().sorted()更快?的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!