Java8 新特性連載番外篇——Stream真的效率低嗎

自從Java8推出之後,Stream新特性就被廣泛關注,我在前幾篇也著重介紹了一下,從編寫代碼角度確實要比之前乾淨、優雅得多,但是有一個問題一直有爭議,那就是性能問題,用了Stream效率會不會降低?真的會出現網上所說的效率低20倍嗎?網上測評文章也很多,莫衷於世,眾說紛紜,這兩天閒來無事,我也對此進行了測試:

測試機器

機器1:雙核8G內存

機器2:四核16G內存

測試數據:一個有10000000個隨機32位整數的ArrayList,實現代碼如下:

List<integer> cache = new ArrayList<>();
Random seed = new Random();
for (int i = 0; i < 10000000; i++) {
cache.add(seed.nextInt());
}
/<integer>

測試項目

項目1:對每個元素開平方根後求和;

常規方法:不用任何Java8的新特性完成任務

double total = 0;
for (Integer i : cache) {
total += Math.sqrt(i);
}

串行流:用stream流方式完成任務

double total = cache.stream().map(Math::sqrt).reduce(Double::sum).orElse(0.);

並行流:用parallelStream並行流完成任務

double total = cache.parallelStream().map(Math::sqrt).reduce(Double::sum).orElse(0.);

項目2:對每個元素開平方根後轉存到另外一個ArrayList

// 常規方法
double total = 0;
List<double> ret = new ArrayList<>();
for (Integer i : cache) {
ret.add(Math.sqrt(i));
}

// 串行流
List<double> ret = cache.stream().map(Math::sqrt).collect(Collectors.toList());

// 並行流
List<double> ret = cache.parallelStream().map(Math::sqrt).collect(Collectors.toList());
/<double>/<double>/<double>

項目3:篩選出3的倍數然後轉存到一個新的ArrayList

// 常規方法
List<integer> ret = new ArrayList<>();
for (Integer i : cache) {
if (i % 3 == 0) {
ret.add(i);
}
}

// 串行流方式
List<integer> ret = cache.stream().filter(i->i%3==0).collect(Collectors.toList());

// 並行流方式
List<integer> ret = cache.parallelStream().filter(i->i%3==0).collect(Collectors.toList());
/<integer>/<integer>/<integer>

項目4:將所有相同元素進行分組並轉存到Map<integer>>/<integer>

// 常規方法
Map<integer>> group = new HashMap<>();
for (Integer i : cache) {

List<integer> g = group.get(i);
if (g == null) {
g = new ArrayList<>();
}
g.add(i);
group.put(i, g);
}

// 串行流方式
cache.stream().collect(Collectors.groupingBy(Function.identity()));

// 並行流方式
cache.parallelStream().collect(Collectors.groupingBy(Function.identity()));
/<integer>/<integer>

實施方案

為防止計算環境變化帶來的不確定性,我們每個測試項目都執行100次,然後統計每次執行時間,單位秒。

測試結果

機器1執行情況:

Java8 新特性連載番外篇——Stream真的效率低嗎

機器1:項目1執行情況

Java8 新特性連載番外篇——Stream真的效率低嗎

機器1:項目2執行情況

Java8 新特性連載番外篇——Stream真的效率低嗎

機器1:項目3執行情況

Java8 新特性連載番外篇——Stream真的效率低嗎

機器1:項目4執行情況

機器1執行情況說明:總體來看,好像串行流表現比較差,我們看看下圖平均值

Java8 新特性連載番外篇——Stream真的效率低嗎

機器1各項目執行平均值比較

情況是不是好很多呢,stream並沒有網傳的那麼糟糕,常規方法也就在項目1中表現優異

***特別說一下:在執行項目4時,由於我的機器1(一臺筆記本)性能較差,執行時間太長了,所以沒有執行100次,就執行了10次。另外,如果按照默認JVM內存設置,並行流執行項目4時直接爆OOM異常!因此有理由認為並行流還是比較耗費資源的,童鞋們在使用時要格外注意。

機器2執行情況:

Java8 新特性連載番外篇——Stream真的效率低嗎

機器2項目1執行情況

Java8 新特性連載番外篇——Stream真的效率低嗎

機器2項目2執行情況

Java8 新特性連載番外篇——Stream真的效率低嗎

機器2項目3執行情況

Java8 新特性連載番外篇——Stream真的效率低嗎

機器2項目4執行情況

Java8 新特性連載番外篇——Stream真的效率低嗎

機器2各項目執行平均值

機器2的執行情況可以看出,常規方法並沒有多少優勢,而且機器機器越好,串行流與常規方法的差距也變得可以忽略不計了。

總結陳詞

1、但凡新事物出現,必然會有支持者和反對者,這是符合辯證的,我們一定不要道聽途說,人云亦云,絕知此事要躬行啊,老祖宗教導的沒錯。至於童鞋們喜歡或習慣於哪種方式,取決於你們,但有一條,就是我們不能排斥新事物,對於新事物我們需要擁抱它、瞭解它、熟悉它,這樣我們才能不斷進步;

2、三種實現方式其實差別並不大,只用其中一種並不會造成很大影響,如果對於性能有苛刻要求,我們可以酌情選擇,不必拘泥於形式。從前面測試我們可以看出,並行計算在絕大多數情況下表現是優秀的,但在項目4的執行情況不管哪臺機器並行流表現都是最差的,我判斷原因應該是雖然是並行處理,但是分組本身這個操作在並行計算完畢之後,還需要合併,這應該是導致效率較差的主要原因。因此,並行流處理在當分塊計算之間如果沒有任何關聯時效率總是最高的。

3、一定要用發展的眼光去看問題,Java之所以推出這麼多新特性,一定是有其必然性和必要性,反正我是肯定比不上這些計算機專家,因此只要有利於我們提高產出效率又可以讓我們寫的代碼乾淨純粹,我們何樂而不為呢?


分享到:


相關文章: