大家好,我是小米,一個熱愛技術分享的小伙伴。今天我們來聊一聊 Java 中的 Stream,以及如何通過 Stream 來提高遍歷集合的效率。lfp28資訊網——每日最新資訊28at.com
lfp28資訊網——每日最新資訊28at.com
lfp28資訊網——每日最新資訊28at.com
什么是Stream?
lfp28資訊網——每日最新資訊28at.com
在開始深入討論之前,我們先來了解一下什么是 Stream。lfp28資訊網——每日最新資訊28at.com
lfp28資訊網——每日最新資訊28at.com
Stream 是 Java 8 中引入的一種新的抽象概念,用于處理數據序列。它為我們提供了一種更加便捷、高效的方式來操作集合數據,實現了函數式編程的特性。在之前的 Java 版本中,我們通常使用迭代器或者循環來處理集合,代碼顯得冗長且難以閱讀。而引入 Stream 后,我們可以采用聲明式的方式描述數據的處理流程,使代碼更加簡潔、清晰。lfp28資訊網——每日最新資訊28at.com
lfp28資訊網——每日最新資訊28at.com
Stream 的本質是一種數據流,它不是一種數據結構,因此不會改變原有的數據集合。相反,它提供了一系列的中間操作和終端操作,這些操作可以被串聯起來形成一條處理流水線。中間操作用于對數據進行轉換和處理,而終端操作則觸發整個處理流程的執行,產生最終的結果。lfp28資訊網——每日最新資訊28at.com
lfp28資訊網——每日最新資訊28at.com
使用 Stream,我們可以輕松進行各種操作,如篩選、映射、過濾、排序等,而無需手動編寫繁瑣的迭代代碼。這種聲明式的編程風格不僅提高了代碼的可讀性,還有助于并行處理,充分發揮多核 CPU 的性能優勢。lfp28資訊網——每日最新資訊28at.com
lfp28資訊網——每日最新資訊28at.com
以下是一個簡單的代碼示例,演示了使用Stream對集合進行過濾、映射和打印操作的好處:lfp28資訊網——每日最新資訊28at.com
lfp28資訊網——每日最新資訊28at.com
lfp28資訊網——每日最新資訊28at.com
lfp28資訊網——每日最新資訊28at.com
lfp28資訊網——每日最新資訊28at.com
圖片lfp28資訊網——每日最新資訊28at.com
lfp28資訊網——每日最新資訊28at.com
lfp28資訊網——每日最新資訊28at.com
lfp28資訊網——每日最新資訊28at.com
lfp28資訊網——每日最新資訊28at.com
lfp28資訊網——每日最新資訊28at.com
這個簡單的示例展示了Stream的優勢,實際應用中,Stream還可以進行更復雜的操作,如分組、排序等,為集合處理提供了更多靈活性。lfp28資訊網——每日最新資訊28at.com
lfp28資訊網——每日最新資訊28at.com
lfp28資訊網——每日最新資訊28at.com
Stream操作分類
lfp28資訊網——每日最新資訊28at.com
在使用 Stream 進行集合操作時,我們通常將其分為兩種操作:中間操作和終端操作。lfp28資訊網——每日最新資訊28at.com
lfp28資訊網——每日最新資訊28at.com
中間操作是在數據源上進行的轉換和處理,但并不立即觸發流的遍歷。這些操作包括 filter、map、distinct 等。通過 filter 我們可以輕松篩選出符合條件的元素,而 map 則用于轉換元素,使得處理過程更為靈活。lfp28資訊網——每日最新資訊28at.com
lfp28資訊網——每日最新資訊28at.com
lfp28資訊網——每日最新資訊28at.com
lfp28資訊網——每日最新資訊28at.com
lfp28資訊網——每日最新資訊28at.com
圖片lfp28資訊網——每日最新資訊28at.com
lfp28資訊網——每日最新資訊28at.com
lfp28資訊網——每日最新資訊28at.com
lfp28資訊網——每日最新資訊28at.com
lfp28資訊網——每日最新資訊28at.com
lfp28資訊網——每日最新資訊28at.com
在上述示例中,filter 用于選擇偶數,map 則將這些偶數平方,形成了中間操作的鏈式調用。lfp28資訊網——每日最新資訊28at.com
lfp28資訊網——每日最新資訊28at.com
終端操作是觸發流的遍歷并產生最終結果的操作,結束流的處理。這些操作包括 forEach、collect、reduce 等。通過 collect 我們可以將流中的元素收集到一個新的集合中。lfp28資訊網——每日最新資訊28at.com
lfp28資訊網——每日最新資訊28at.com
lfp28資訊網——每日最新資訊28at.com
lfp28資訊網——每日最新資訊28at.com
lfp28資訊網——每日最新資訊28at.com
圖片lfp28資訊網——每日最新資訊28at.com
lfp28資訊網——每日最新資訊28at.com
lfp28資訊網——每日最新資訊28at.com
lfp28資訊網——每日最新資訊28at.com
lfp28資訊網——每日最新資訊28at.com
lfp28資訊網——每日最新資訊28at.com
在這個示例中,collect 將處理后的結果收集到一個新的列表中,結束了整個流的處理過程。lfp28資訊網——每日最新資訊28at.com
lfp28資訊網——每日最新資訊28at.com
lfp28資訊網——每日最新資訊28at.com
Stream源碼實現
lfp28資訊網——每日最新資訊28at.com
Stream 的源碼實現是 Java 8 中引入的一項復雜而精妙的特性,它為處理集合數據提供了一種全新的方式。在深入探討 Stream 的源碼實現之前,我們首先需要了解幾個關鍵的類和接口,它們構成了 Stream 操作的基礎結構。lfp28資訊網——每日最新資訊28at.com
lfp28資訊網——每日最新資訊28at.com
首先,BaseStream 接口是 Stream API 中的基礎,它定義了一些基本的操作,例如串行執行和并行執行。這個接口為不同類型的 Stream,如 Stream、IntStream、DoubleStream 等提供了一致的接口定義,使得操作在不同類型的流之間能夠得到復用。lfp28資訊網——每日最新資訊28at.com
lfp28資訊網——每日最新資訊28at.com
接著,AbstractPipeline 類是 Stream 的核心類之一,它封裝了操作的基本邏輯,包括遍歷、過濾等。這個類為具體的操作提供了抽象基類,簡化了新操作的添加。它還定義了流水線的基本結構,使得我們能夠串聯多個操作形成一個完整的處理流程。lfp28資訊網——每日最新資訊28at.com
lfp28資訊網——每日最新資訊28at.com
在針對對象引用流的處理中,ReferencePipeline 繼承自 AbstractPipeline,通過一系列方法(如 filter、map 等)生成不同類型的中間操作,形成操作鏈。而 Sink 類則負責接收元素并進行實際的處理。這種流水線的設計充分體現了函數式編程的思想,每個操作都是不可變的,而且在進行終端操作前,中間操作只是構建了一個操作鏈而并未實際執行。lfp28資訊網——每日最新資訊28at.com
lfp28資訊網——每日最新資訊28at.com
在具體的操作實現中,以 filter 為例,通過 ReferencePipeline 類的 filter 方法生成一個新的流水線,其中定義了過濾的邏輯,形成了一個中間操作。這個設計使得我們能夠以鏈式的方式組織多個操作,從而更加靈活地構建數據處理流程。lfp28資訊網——每日最新資訊28at.com
lfp28資訊網——每日最新資訊28at.com
lfp28資訊網——每日最新資訊28at.com
Stream操作疊加源碼解析
lfp28資訊網——每日最新資訊28at.com
在實際應用中,我們常常需要對集合進行多個操作,這時候就涉及到 Stream 操作的疊加。通過源碼解析,我們可以深入了解這一過程的執行。lfp28資訊網——每日最新資訊28at.com
lfp28資訊網——每日最新資訊28at.com
首先,讓我們看一下一個簡單的例子:lfp28資訊網——每日最新資訊28at.com
lfp28資訊網——每日最新資訊28at.com
lfp28資訊網——每日最新資訊28at.com
lfp28資訊網——每日最新資訊28at.com
lfp28資訊網——每日最新資訊28at.com
圖片lfp28資訊網——每日最新資訊28at.com
lfp28資訊網——每日最新資訊28at.com
lfp28資訊網——每日最新資訊28at.com
lfp28資訊網——每日最新資訊28at.com
lfp28資訊網——每日最新資訊28at.com
這個例子中,我們對數字集合進行了篩選(filter)和映射(mapToInt)的兩個操作,然后求和。讓我們逐步分析這個過程。lfp28資訊網——每日最新資訊28at.com
lfp28資訊網——每日最新資訊28at.com
filter操作
首先,filter 操作創建了一個新的 Stream,其中包含了符合條件的元素。這是通過 ReferencePipeline 類的 filter 方法實現的,具體代碼如下:lfp28資訊網——每日最新資訊28at.com
lfp28資訊網——每日最新資訊28at.com
lfp28資訊網——每日最新資訊28at.com
lfp28資訊網——每日最新資訊28at.com
lfp28資訊網——每日最新資訊28at.com
圖片lfp28資訊網——每日最新資訊28at.com
lfp28資訊網——每日最新資訊28at.com
lfp28資訊網——每日最新資訊28at.com
lfp28資訊網——每日最新資訊28at.com
lfp28資訊網——每日最新資訊28at.com
lfp28資訊網——每日最新資訊28at.com
這段代碼展示了如何創建一個新的 Stream,其中的 Sink 對象通過 predicate.test(u) 來判斷是否滿足條件,然后將符合條件的元素傳遞給下游。lfp28資訊網——每日最新資訊28at.com
lfp28資訊網——每日最新資訊28at.com
mapToInt操作
接著,mapToInt 操作對上一個操作的結果進行了映射,將元素乘以2。這是通過 ReferencePipeline 類的 mapToInt 方法實現的,具體代碼如下:lfp28資訊網——每日最新資訊28at.com
lfp28資訊網——每日最新資訊28at.com
lfp28資訊網——每日最新資訊28at.com
lfp28資訊網——每日最新資訊28at.com
lfp28資訊網——每日最新資訊28at.com
圖片lfp28資訊網——每日最新資訊28at.com
lfp28資訊網——每日最新資訊28at.com
lfp28資訊網——每日最新資訊28at.com
lfp28資訊網——每日最新資訊28at.com
lfp28資訊網——每日最新資訊28at.com
lfp28資訊網——每日最新資訊28at.com
這段代碼展示了如何創建一個新的 IntStream,其中的 Sink 對象通過 mapper.applyAsInt(u) 來進行映射操作,將元素乘以2后傳遞給下游。lfp28資訊網——每日最新資訊28at.com
lfp28資訊網——每日最新資訊28at.com
sum操作
最后,sum 操作對上一個操作的結果進行了求和。這是通過 SummingInt 類的 evaluate 方法實現的,具體代碼如下:lfp28資訊網——每日最新資訊28at.com
lfp28資訊網——每日最新資訊28at.com
lfp28資訊網——每日最新資訊28at.com
lfp28資訊網——每日最新資訊28at.com
lfp28資訊網——每日最新資訊28at.com
圖片lfp28資訊網——每日最新資訊28at.com
lfp28資訊網——每日最新資訊28at.com
lfp28資訊網——每日最新資訊28at.com
lfp28資訊網——每日最新資訊28at.com
lfp28資訊網——每日最新資訊28at.com
這段代碼展示了如何對映射后的元素進行求和操作,最終得到結果。lfp28資訊網——每日最新資訊28at.com
lfp28資訊網——每日最新資訊28at.com
通過這個簡單的例子,我們可以看到 Stream 操作的疊加是通過創建新的 Stream,并在每個操作的 Sink 中對元素進行處理和傳遞的。這種鏈式調用的方式使得我們可以靈活組合多個操作,構建出復雜的數據處理流程。lfp28資訊網——每日最新資訊28at.com
lfp28資訊網——每日最新資訊28at.com
lfp28資訊網——每日最新資訊28at.com
Stream并行處理源碼解析
lfp28資訊網——每日最新資訊28at.com
Stream 的一個顯著特點是能夠支持并行處理。在多核 CPU 的環境下,Stream 的并行迭代方式可以顯著提高性能。通過分析源碼,我們可以了解并行處理是如何實現的,以及在何種場景下使用更為合適。lfp28資訊網——每日最新資訊28at.com
lfp28資訊網——每日最新資訊28at.com
首先,讓我們看一個簡單的例子:lfp28資訊網——每日最新資訊28at.com
lfp28資訊網——每日最新資訊28at.com
lfp28資訊網——每日最新資訊28at.com
lfp28資訊網——每日最新資訊28at.com
lfp28資訊網——每日最新資訊28at.com
圖片lfp28資訊網——每日最新資訊28at.com
lfp28資訊網——每日最新資訊28at.com
lfp28資訊網——每日最新資訊28at.com
lfp28資訊網——每日最新資訊28at.com
lfp28資訊網——每日最新資訊28at.com
lfp28資訊網——每日最新資訊28at.com
在這個例子中,我們使用了 parallelStream() 方法將 Stream 轉換為并行流,然后進行映射和求和操作。接下來,我們將逐步分析這個過程。lfp28資訊網——每日最新資訊28at.com
lfp28資訊網——每日最新資訊28at.com
parallelStream操作
首先,parallelStream() 方法是通過 BaseStream 接口的 parallel() 方法實現的,具體代碼如下:lfp28資訊網——每日最新資訊28at.com
lfp28資訊網——每日最新資訊28at.com
lfp28資訊網——每日最新資訊28at.com
lfp28資訊網——每日最新資訊28at.com
lfp28資訊網——每日最新資訊28at.com
圖片lfp28資訊網——每日最新資訊28at.com
lfp28資訊網——每日最新資訊28at.com
lfp28資訊網——每日最新資訊28at.com
lfp28資訊網——每日最新資訊28at.com
lfp28資訊網——每日最新資訊28at.com
lfp28資訊網——每日最新資訊28at.com
這段代碼通過 StreamSupport.stream(spliterator(), true) 來創建一個支持并行的 Stream。lfp28資訊網——每日最新資訊28at.com
lfp28資訊網——每日最新資訊28at.com
并行處理的實現
在并行處理過程中,Stream 會被分割成多個子任務,每個子任務在一個獨立的線程中執行。這是通過 ForkJoinTask 框架實現的,具體代碼如下:lfp28資訊網——每日最新資訊28at.com
lfp28資訊網——每日最新資訊28at.com
lfp28資訊網——每日最新資訊28at.com
lfp28資訊網——每日最新資訊28at.com
lfp28資訊網——每日最新資訊28at.com
圖片lfp28資訊網——每日最新資訊28at.com
lfp28資訊網——每日最新資訊28at.com
lfp28資訊網——每日最新資訊28at.com
lfp28資訊網——每日最新資訊28at.com
lfp28資訊網——每日最新資訊28at.com
lfp28資訊網——每日最新資訊28at.com
invoke() 方法用于執行任務,每個子任務都是一個 ForkJoinTask,它們會在多個線程中同時執行,最后將結果合并起來。lfp28資訊網——每日最新資訊28at.com
lfp28資訊網——每日最新資訊28at.com
并行處理的Sink
在并行處理中,每個子任務都有自己的 Sink 對象,用于處理元素。這是通過 ForkingSink 類實現的,具體代碼如下:lfp28資訊網——每日最新資訊28at.com
lfp28資訊網——每日最新資訊28at.com
lfp28資訊網——每日最新資訊28at.com
lfp28資訊網——每日最新資訊28at.com
lfp28資訊網——每日最新資訊28at.com
圖片lfp28資訊網——每日最新資訊28at.com
lfp28資訊網——每日最新資訊28at.com
lfp28資訊網——每日最新資訊28at.com
lfp28資訊網——每日最新資訊28at.com
lfp28資訊網——每日最新資訊28at.com
ForkingSink 中的 accept() 方法用于接收元素,然后通過 split() 方法將任務進行分割。lfp28資訊網——每日最新資訊28at.com
lfp28資訊網——每日最新資訊28at.com
通過這個簡單的例子,我們可以看到 Stream 的并行處理是通過 ForkJoin 框架實現的,每個子任務都在獨立的線程中執行,最后將結果合并。這種方式能夠更好地利用多核 CPU 的性能,提高處理速度。lfp28資訊網——每日最新資訊28at.com
性能測試
lfp28資訊網——每日最新資訊28at.com
為了更直觀地比較兩者的性能,我們使用JMH(Java Microbenchmarking Harness)進行測試。lfp28資訊網——每日最新資訊28at.com
lfp28資訊網——每日最新資訊28at.com
以下是一個簡單的示例代碼,假設我們有一個包含一系列數字的列表,我們將對這些數字進行過濾,然后按照奇偶性進行分組:lfp28資訊網——每日最新資訊28at.com
lfp28資訊網——每日最新資訊28at.com
lfp28資訊網——每日最新資訊28at.com
lfp28資訊網——每日最新資訊28at.com
lfp28資訊網——每日最新資訊28at.com
圖片lfp28資訊網——每日最新資訊28at.com
lfp28資訊網——每日最新資訊28at.com
lfp28資訊網——每日最新資訊28at.com
lfp28資訊網——每日最新資訊28at.com
lfp28資訊網——每日最新資訊28at.com
lfp28資訊網——每日最新資訊28at.com
測試結論:lfp28資訊網——每日最新資訊28at.com
lfp28資訊網——每日最新資訊28at.com
lfp28資訊網——每日最新資訊28at.com
lfp28資訊網——每日最新資訊28at.com
lfp28資訊網——每日最新資訊28at.com
圖片lfp28資訊網——每日最新資訊28at.com
lfp28資訊網——每日最新資訊28at.com
lfp28資訊網——每日最新資訊28at.com
lfp28資訊網——每日最新資訊28at.com
lfp28資訊網——每日最新資訊28at.com
通過以上測試結果,我們可以看到:lfp28資訊網——每日最新資訊28at.com
- 在循環迭代次數較少的情況下,常規的迭代方式性能反而更好;
- 在單核 CPU 服務器配置環境中,也是常規迭代方式更有優勢;
- 而在大數據循環迭代中,如果服務器是多核 CPU 的情況下,Stream 的并行迭代優勢明顯。
所以我們在平時處理大數據的集合時,應該盡量考慮將應用部署在多核 CPU 環境下,并且使用 Stream 的并行迭代方式進行處理。lfp28資訊網——每日最新資訊28at.com
總結
lfp28資訊網——每日最新資訊28at.com
用事實說話,我們看到其實使用 Stream 未必可以使系統性能更佳,還是要結合應用場景進行選擇,也就是合理地使用 Stream。lfp28資訊網——每日最新資訊28at.com
lfp28資訊網——每日最新資訊28at.com
總的來說,Stream 是一個強大而靈活的工具,但并不是適用于所有場景。在選擇使用 Stream 時,我們需要根據實際情況進行權衡和取舍。lfp28資訊網——每日最新資訊28at.com
lfp28資訊網——每日最新資訊28at.com
通過深入了解 Stream 的底層實現,我們可以更好地運用這一特性,提高代碼的可讀性和性能。lfp28資訊網——每日最新資訊28at.com
本文鏈接:http://www.www897cc.com/showinfo-26-68328-0.html性能篇:解密Stream,提升集合遍歷效率的秘訣!
聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯系,我們將在第一時間刪除處理。郵件:2376512515@qq.com
上一篇: Spring實現Kafka重試Topic,真的太香了
下一篇: Python的Graphlib庫,再也不用手敲圖結構了