簡潔又快速地處理集合——Java8 Stream(上)

Java 8 發佈至今也已經好幾年過去,如今 Java 也已經向 11 邁去,但是 Java 8 作出的改變可以說是革命性的,影響足夠深遠,學習 Java 8 應該是 Java 開發者的必修課。

值得注意的是:學習 Stream 之前必須先學習 lambda 的相關知識。本文也假設讀者已經掌握 lambda 的相關知識

本篇文章主要內容:

  • 介紹 Stream 以及 Stream 是如何處理集合的
  • 介紹 Stream 與集合的關係與區別

本篇文章主要是讓大家能夠理解 Stream,理解它的基本原理,理解我們爲什麼需要使用 Stream 以及它的好處,而具體的實戰環節我會在下篇文章中講解。

 

一. 什麼是 Stream

Stream 中文稱爲 “流”,通過將集合轉換爲這麼一種叫做 “流” 的元素序列,通過聲明性方式,能夠對集合中的每個元素進行一系列並行或串行的流水線操作

換句話說,你只需要告訴流你的要求,流便會在背後自行根據要求對元素進行處理,而你只需要 “坐享其成”。

二. 流操作

整個流操作就是一條流水線,將元素放在流水線上一個個地進行處理。

其中數據源便是原始集合,然後將如 List 的集合轉換爲 Stream 類型的流,並對流進行一系列的中間操作,比如過濾保留部分元素、對元素進行排序、類型轉換等;最後再進行一個終端操作,可以把 Stream 轉換回集合類型,也可以直接對其中的各個元素進行處理,比如打印、比如計算總數、計算最大值等等

很重要的一點是,很多流操作本身就會返回一個流,所以多個操作可以直接連接起來,我們來看看一條 Stream 操作的代碼:

如果是以前,進行這麼一系列操作,你需要做個迭代器或者 foreach 循環,然後遍歷,一步步地親力親爲地去完成這些操作;但是如果使用流,你便可以直接聲明式地下指令,流會幫你完成這些操作。

有沒有想到什麼類似的?是的,就像 SQL 語句一樣, 

select username from user where id = 1

,你只要說明:“我需要 id 是 1 (id = 1)的用戶(user)的用戶名(username )”,那麼就可以得到自己想要的數據,而不需要自己親自去數據庫裏面循環遍歷查找。

三. 流與集合

什麼時候計算

Stream 和集合的其中一個差異在於什麼時候進行計算。

一個集合,它會包含當前數據結構中所有的值,你可以隨時增刪,但是集合裏面的元素毫無疑問地都是已經計算好了的。

流則是按需計算,你可以想象一個水龍頭,假設你需要一個奇數流,從 1 開始,那麼這個水龍頭會源源不斷地流出你需要的數據,假設你只需要 10 個,那麼這個流便會按需生成 10 個奇數,換句話來說,就是在用戶要求的時候纔會計算值,只要你需要,你便可以打開這個水龍頭。

又比方說我們通過搜索引擎進行搜索,搜索出來的條目並不是全部呈現出來的,而且先顯示最符合的前 10 條或者前 20 條,只有在點擊 “下一頁” 的時候,纔會再輸出新的 10 條。

再比方在線觀看電影和你硬盤裏面的電影,也是差不多的道理。

外部迭代和內部迭代

Stream 和集合的另一個差異在於迭代。

我們可以把集合比作一個工廠的倉庫,一開始工廠比較落後,要對貨物作什麼修改,只能工人親自走進倉庫對貨物進行處理,有時候還要將處理後的貨物放到一個新的倉庫裏面。在這個時期,我們需要親自去做迭代,一個個地找到需要的貨物,並進行處理,這叫做外部迭代

後來工廠發展了起來,配備了流水線作業,只要根據需求設計出相應的流水線,然後工人只要把貨物放到流水線上,就可以等着接收成果了,而且流水線還可以根據要求直接把貨物輸送到相應的倉庫。這就叫做內部迭代,流水線已經幫你把迭代給完成了,你只需要說要幹什麼就可以了(即設計出合理的流水線)。

Java 8 引入 Stream 很大程度是因爲,流的內部迭代可以自動選擇一種合適你硬件的數據表示和並行實現;而以往程序員自己進行 foreach 之類的時候,則需要自己去管理並行等問題。

一次性的流

流和迭代器類似,只能迭代一次。

Stream<String> stream = list.stream().map(Person::getName).sorted().limit(10);         
List<String> newList = stream.collect(toList());
List<String> newList2 = stream.collect(toList());

上面代碼中第三行會報錯,因爲第二行已經使用過這個流,這個流已經被消費掉了

四. 關於並行

我們通過 list.stream() 將 List 類型轉換爲流類型,我們還可以通過 list.parallelStream() 轉換爲並行流。

並行流就是把內容分成多個數據塊,使用不同的線程分別處理每個數據塊的流。這也是流的一大特點,要知道,在 Java 7 之前,並行處理數據集合是非常麻煩的,你得自己去將數據分割開,自己去分配線程,必要時還要確保同步避免競爭。

Stream 讓程序員能夠比較輕易地實現對數據集合的並行處理,但要注意的是,不是所有情況的適合,有些時候並行甚至比順序進行效率更低,而有時候因爲線程安全問題,還可能導致數據的處理錯誤,這些我會在下一篇文章中講解。

發佈了11 篇原創文章 · 獲贊 19 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章