Java8 中用法優雅的 Stream,性能也「優雅」嗎?

2020-12-20 酷扯兒

本文轉載自【微信公眾號:java進階架構師,ID:java_jiagoushi】經微信公眾號授權轉載,如需轉載與原文作者聯繫

ava8的Stream API可以極大提高Java程式設計師的生產力,讓程式設計師寫出高效率、乾淨、簡潔的代碼。那麼,Stream API的性能到底如何呢,代碼整潔的背後是否意味著性能的損耗呢?本文對Stream API的性能一探究竟。

為保證測試結果真實可信,我們將JVM運行在-server模式下,測試數據在GB量級,測試機器採用常見的商用伺服器,配置如下:

測試方法與數據

性能測試並不是容易的事,Java性能測試更費勁,因為虛擬機對性能的影響很大,JVM對性能的影響有兩方面:

GC的影響。GC的行為是Java中很不好控制的一塊,為增加確定性,我們手動指定使用CMS收集器,並使用10GB固定大小的堆內存。具體到JVM參數就是 -XX:+UseConcMarkSweepGC-Xms10G-Xmx10GJIT(Just-In-Time)即時編譯技術。即時編譯技術會將熱點代碼在JVM運行的過程中編譯成本地代碼,測試時我們會先對程序預熱,觸發對測試函數的即時編譯。相關的JVM參數是 -XX:CompileThreshold=10000Stream並行執行時用到

ForkJoinPool.commonPool()

得到的線程池,為控制並行度我們使用Linux的

taskset

命令指定JVM可用的核數。

測試數據由程序隨機生成。為防止一次測試帶來的抖動,測試4次求出平均時間作為運行時間。

基本類型迭代

測試內容:找出整型數組中的最小值。對比for循環外部迭代和Stream API內部迭代性能。

測試程序IntTest,測試結果如下圖:

圖中展示的是for循環外部迭代耗時為基準的時間比值。分析如下:

對於基本類型Stream串行迭代的性能開銷明顯高於外部迭代開銷(兩倍);Stream並行迭代的性能比串行迭代和外部迭代都好。並行迭代性能跟可利用的核數有關,上圖中的並行迭代使用了全部12個核,為考察使用核數對性能的影響,我們專門測試了不同核數下的Stream並行迭代效果:

分析,對於基本類型:

使用Stream並行API在單核情況下性能很差,比Stream串行API的性能還差;隨著使用核數的增加,Stream並行效果逐漸變好,比使用for循環外部迭代的性能還好。以上兩個測試說明,對於基本類型的簡單迭代,Stream串行迭代性能更差,但多核情況下Stream迭代時性能較好。

對象迭代

接下來我們再來看對象的迭代效果。

測試內容:找出字符串列表中最小的元素(自然順序),對比for循環外部迭代和Stream API內部迭代性能。

測試程序StringTest,測試結果如下圖:

結果分析如下:

對於對象類型Stream串行迭代的性能開銷仍然高於外部迭代開銷(1.5倍),但差距沒有基本類型那麼大。Stream並行迭代的性能比串行迭代和外部迭代都好。再來單獨考察Stream並行迭代效果:

分析,對於對象類型:

使用Stream並行API在單核情況下性能比for循環外部迭代差;隨著使用核數的增加,Stream並行效果逐漸變好,多核帶來的效果明顯。以上兩個測試說明,對於對象類型的簡單迭代,Stream串行迭代性能更差,但多核情況下Stream迭代時性能較好。

複雜對象歸約

從實驗一、二的結果來看,Stream串行執行的效果都比外部迭代差(很多),是不是說明Stream真的不行了?先別下結論,我們再來考察一下更複雜的操作。

測試內容:給定訂單列表,統計每個用戶的總交易額。對比使用外部迭代手動實現和Stream API之間的性能。

我們將訂單簡化為

<userName,price,timeStamp>

構成的元組,並用

Order

對象來表示。測試程序ReductionTest,測試結果如下圖:

分析,對於複雜的歸約操作:

Stream API的性能普遍好於外部手動迭代,並行Stream效果更佳;再來考察並行度對並行效果的影響,測試結果如下:

分析,對於複雜的歸約操作:

使用Stream並行歸約在單核情況下性能比串行歸約以及手動歸約都要差,簡單說就是最差的;隨著使用核數的增加,Stream並行效果逐漸變好,多核帶來的效果明顯。以上兩個實驗說明,對於複雜的歸約操作,Stream串行歸約效果好於手動歸約,在多核情況下,並行歸約效果更佳。我們有理由相信,對於其他複雜的操作,Stream API也能表現出相似的性能表現。

結論

上述三個實驗的結果可以總結如下:

對於簡單操作,比如最簡單的遍歷,Stream串行API性能明顯差於顯示迭代,但並行的Stream API能夠發揮多核特性。對於複雜操作,Stream串行API性能可以和手動實現的效果匹敵,在並行執行時Stream API效果遠超手動實現。所以,如果出於性能考慮,

對於簡單操作推薦使用外部迭代手動實現對於複雜操作,推薦使用Stream API,在多核情況下,推薦使用並行Stream API來發揮多核優勢單核情況下不建議使用並行Stream API如果出於代碼簡潔性考慮,使用Stream API能夠寫出更短的代碼。即使是從性能方面說,儘可能的使用Stream API也另外一個優勢,那就是只要Java Stream類庫做了升級優化,代碼不用做任何修改就能享受到升級帶來的好處。

相關焦點

  • java集合【6】——— Iterable接口
    JamException in thread "main" java.util.ConcurrentModificationException at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:909) at java.util.ArrayList
  • 如何優雅的設計 Java 異常
    本文轉載自【微信公眾號:java進階架構師,ID:java_jiagoushi】經微信公眾號授權轉載,如需轉載與原文作者聯繫導語異常處理是程序開發中必不可少操作之一,但如何正確優雅的對異常進行處理確是一門學問
  • 【Java】Java8之流Stream
    1 匹配聚合操作 allMatch:接收一個 Predicate 函數,當流中每個元素都符合該斷言時才返回true,否則返回false noneMatch:接收一個 Predicate 函數,當流中每個元素都不符合該斷言時才返回true,否則返回false anyMatch:接收一個 Predicate 函數,只要流中有一個元素滿足該斷言則返回
  • 《荒島餘生》:湯姆·漢克斯是如何優雅地結束一段親密關係的?
    從右側流程組件下選擇常規操作 -> 循環拖動到中間設計區域的流程線中 保存數據流 節點後:變量名 :saved_streams輸出變量名稱:stream6.從右側流程組件下選擇常規操作 -> JavaAction拖動到中間設計區域的流程線中循環節點中:JavaAction :增加數據流數量設置參數:currentstream - $stream節點說明:更新數據流數量輸出變量名稱
  • java優先隊列PriorityQueue中Comparator的用法
    在使用java的優先隊列PriorityQueue的時候,會看到這樣的用法。註:在siftUp中,如果是最小堆,那麼應該是較小的元素往上走,如果是最大堆,則應該是較大的元素往上走。由於源碼中新入隊元素x是在第1個參數的位置,因此最大/最小優先隊列主要根據第1個參數的大小關係來判斷。int compare(Integer x, Integer e){ return x > e ?
  • 阿里內部學習指南《Effective Java中文 第3版》程式設計師進階必備
    經典Jolt獲獎作品《Effective Java》的第3版這本書,對上一版內容進行了徹底的更新,介紹了如何充分利用從泛型到枚舉、從註解到自動裝箱的各種特性,幫助讀者更加有效地使用Java程式語言及其基本類庫:java.lang. java.util和java.io,以及子包,如java.util. concurrent和java.util.function等。
  • 巧用Java8中的Stream,讓集合操作飛起來!
    本文轉載自【微信公眾號:java進階架構師,ID:java_jiagoushi】經微信公眾號授權轉載,如需轉載與原文作者聯繫作者:堅持就是勝利juejin.im/post/5d5e2616f265da03b638b28a簡介
  • Java中的Set、List、Map的用法與區別
    Set的用法:存放的是對象的引用,沒有重複對象Set set=new HashSet(); String s1=new String("hello"); String s2=s1; String s3=new String("world");
  • Java:如何更優雅地處理空值?
    本文轉載自【微信公眾號:java進階架構師,ID:java_jiagoushi】經微信公眾號授權轉載,如需轉載與原文作者聯繫導語在筆者幾年的開發經驗中,經常看到項目中存在到處空值判斷的情況,這些判斷,會讓人覺得摸不著頭緒,它的出現很有可能和當前的業務邏輯並沒有關係。
  • 這些 Java 8 官方挖的坑,你踩過幾個?
    到了Java 8後,Base64編碼已經成為Java類庫的標準,內置了 Base64 編碼的編碼器和解碼器。Caused by: java.lang.IllegalArgumentException: Illegal base64 character 3f    at java.util.Base64$Decoder.decode0(Base64.java:714)    at java.util.Base64$Decoder.decode
  • Java 枚舉(enum) 詳解7種常見的用法
    在實際編程中,往往存在著這樣的「數據集」,它們的數值在程序中是穩定的,而且「數據集」中的元素是有限的。
  • java集合詳解合集
    所以的集合類都位於java.util包下,後來為了處理多線程環境下的並發安全問題,java5還在java.util.concurrent包下提供了一些多線程支持的集合類。在學習Java中的集合類的API、編程原理的時候,我們一定要明白,"集合"是一個很古老的數學概念,它遠遠早於Java的出現。
  • Java基礎面試題簡單總結
    為了提高性能,在軟體發布後,assertion檢查通常是關閉的7、String s = new String("xyz");創建了幾個String Object答:兩個,一個字符對象,一個字符對象引用對象8、
  • 優雅的女生如何拍照?學會這8個簡單姿勢,就能拍出自然優雅美
    優雅,是很多女生都喜歡體現的一種魅力,給人氣質舒服的感覺,那麼,在拍照的時候,應該怎麼擺拍姿勢才能拍出女生的自然優雅美呢?今天跟著小編來學習8個簡單拍照姿勢,輕鬆拍出你的優雅美。
  • 黑馬程式設計師:Java學科-引用類型用法總結以遊戲為案例
    本文內容引用類型文章輸出能夠理解引用類型作為成員變量能夠理解引用類型作為方法參數能夠理解引用類型作為方法返回值類型一、引用類型用法總結在實際的開發當中,引用類型的使用是很重要和普遍的。ArrayList類我們都知道,查看API的話我們可以發現,實際上是java.utli.List 接口實現類。所以,當我們看見List接口作為參數或者返回值類型的時候,當然所以可以將ArratList的對象進行傳遞或者是返回。
  • 優雅女生如何拍照,8個簡單姿勢學一學,就能拍出自然優雅美
    優雅,是很多女生都喜歡體現的一種魅力,給人舒服氣質的感覺,那麼,在拍照的時候,應該怎麼擺拍姿勢才能拍出女生的自然優雅美呢?今天跟著小編來學習8個簡單拍照姿勢,輕鬆拍出你的優雅美。1、側看鏡頭,雙手放身後女生想要拍照拍出優雅的效果,可以讓自己像下面的女生一樣,側身對著鏡頭,眼神自然回頭看著鏡頭,然後雙手自然藏在身後,這樣可以讓你的姿態更加自然好看,身材也好,拍出自然優雅的感覺。
  • Java中設置classpath、path、JAVA_HOME的作用?
    path:Windows系統執行命令時要搜尋的路徑在dos命令行中執行命令的時候,會先從當前路徑去找,如果找不到,就會到path路徑下去找比如我的jdk裝在D盤下,當我在C盤執行java -version的時候會出現找不到java命令的情況,設置path的目的就是當在
  • 浪琴表2013巴塞爾新品:優雅、運動與傳統的傑作
    康鉑系列因世界賽馬盛世而生;康卡斯潛水系列演繹水下世界的絢爛多姿;嘉嵐系列百鑽腕錶呈獻永恆優雅與璀璨鑽石的無盡魅力;經典復刻系列1938軍事腕錶則在歷史中探索力量與衝突之美。
  • 【答疑解惑】java中static關鍵字的作用
    static方法static方法一般稱作靜態方法,由於靜態方法不依賴於任何對象就可以進行訪問