日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

JDK1.8 升级这么久!Stream 流的规约操作有哪些?

發布時間:2024/3/13 编程问答 51 豆豆
生活随笔 收集整理的這篇文章主要介紹了 JDK1.8 升级这么久!Stream 流的规约操作有哪些? 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

前段時間介紹了部分?Stream常見接口方法,理解起來并不困難,但Stream的用法不止于此,本節我們將仍然以Stream為例,介紹流的規約操作。



規約操作又被稱作折疊操作,是通過某個連接動作將所有元素匯總成一個匯總結果的過程。元素求和、求最大值或最小值、求出元素總個數、將所有元素轉換成一個列表或集合,都屬于規約操作。Stream類庫有兩個通用的規約操作reduce()和collect(),也有一些為簡化書寫而設計的專用規約操作,比如sum()、max()、min()、count()等。



最大或最小值這類規約操作很好理解(至少方法語義上是這樣),我們著重介紹reduce()和collect(),這是比較有魔法的地方。



多面手reduce()

reduce操作可以實現從一組元素中生成一個值,sum()、max()、min()、count()等都是reduce操作,將他們單獨設為函數只是因為常用。reduce()的方法定義有三種重寫形式:

  • Optional<T> reduce(BinaryOperator<T> accumulator)

  • T reduce(T identity, BinaryOperator<T> accumulator)

  • <U> U reduce(U identity, BiFunction<U,? super T,U> accumulator, BinaryOperator<U> combiner)





雖然函數定義越來越長,但語義不曾改變,多的參數只是為了指明初始值,或者是指定并行執行時多個部分結果的合并方式。reduce()最常用的場景就是從一堆值中生成一個值。用這么復雜的函數去求一個最大或最小值,你是不是覺得設計者有病。其實不然,因為“大”和“小”或者“求和"有時會有不同的語義。

需求:從一組單詞中找出最長的單詞。這里“大”的含義就是“長”。



上述代碼會選出最長的單詞love,其中Optional是(一個)值的容器,使用它可以避免null值的麻煩。當然可以使用Stream.max(Comparator<? super T> comparator)方法來達到同等效果,但reduce()自有其存在的理由。



上述代碼標號(2)處將i. 字符串映射成長度,ii. 并和當前累加和相加。這顯然是兩步操作,使用reduce()函數將這兩步合二為一,更有助于提升性能。如果想要使用map()和sum()組合來達到上述目的,也是可以的。



reduce()擅長的是生成一個值,如果想要從Stream生成一個集合或者Map等復雜的對象該怎么辦呢?終極武器collect()橫空出世!



終極武器collect()

不夸張的講,如果你發現某個功能在Stream接口中沒找到,十有八九可以通過collect()方法實現。collect()是Stream接口方法中最靈活的一個,學會它才算真正入門Java函數式編程。先看幾個熱身的小例子:



上述代碼分別列舉了如何將Stream轉換成List、Set和Map。雖然代碼語義很明確,可是我們仍然會有幾個疑問:

  • Function.identity()是干什么的?

  • String::length是什么意思?

  • Collectors是個什么東西?



接口的靜態方法和默認方法

Function是一個接口,那么Function.identity()是什么意思呢?這要從兩方面解釋:

  • Java 8允許在接口中加入具體方法。接口中的具體方法有兩種,default方法和static方法,identity()就是Function接口的一個靜態方法。

  • Function.identity()返回一個輸出跟輸入一樣的Lambda表達式對象,等價于形如t -> t形式的Lambda表達式。



上面的解釋是不是讓你疑問更多?不要問我為什么接口中可以有具體方法,也不要告訴我你覺得t -> t比identity()方法更直觀。我會告訴你接口中的default方法是一個無奈之舉,在Java 7及之前要想在定義好的接口中加入新的抽象方法是很困難甚至不可能的,因為所有實現了該接口的類都要重新實現。試想在Collection接口中加入一個stream()抽象方法會怎樣?default方法就是用來解決這個尷尬問題的,直接在接口中實現新加入的方法。既然已經引入了default方法,為何不再加入static方法來避免專門的工具類呢!



方法引用

諸如String::length的語法形式叫做方法引用(method references),這種語法用來替代某些特定形式Lambda表達式。如果Lambda表達式的全部內容就是調用一個已有的方法,那么可以用方法引用來替代Lambda表達式。方法引用可以細分為四類:





收集器

相信前面繁瑣的內容已徹底打消了你學習Java函數式編程的熱情,不過很遺憾,下面的內容更繁瑣。但這不能怪Stream類庫,因為要實現的功能本身很復雜。

收集器(Collector)是為Stream.collect()方法量身打造的工具接口(類)。考慮一下將一個Stream轉換成一個容器(或者Map)需要做哪些工作?我們至少需要兩樣東西:

  • 目標容器是什么?是ArrayList還是HashSet,或者是個TreeMap。

  • 新元素如何添加到容器中?是List.add()還是Map.put()。

如果并行的進行規約,還需要告訴collect()?3. 多個部分結果如何合并成一個。



結合以上分析,collect()方法定義為<R> R collect(Supplier<R> supplier, BiConsumer<R,? super T> accumulator, BiConsumer<R,R> combiner),三個參數依次對應上述三條分析。不過每次調用collect()都要傳入這三個參數太麻煩,收集器Collector就是對這三個參數的簡單封裝,所以collect()的另一定義為<R,A> R collect(Collector<? super T,A,R> collector)。Collectors工具類可通過靜態方法生成各種常用的Collector。舉例來說,如果要將Stream規約成List可以通過如下兩種方式實現:





通常情況下我們不需要手動指定collect()的三個參數,而是調用collect(Collector<? super T,A,R> collector)方法,并且參數中的Collector對象大都是直接通過Collectors工具類獲得。實際上傳入的收集器的行為決定了collect()的行為



使用collect()生成Collection

前面已經提到通過collect()方法將Stream轉換成容器的方法,這里再匯總一下。將Stream轉換成List或Set是比較常見的操作,所以Collectors工具已經為我們提供了對應的收集器,通過如下代碼即可完成:





上述代碼能夠滿足大部分需求,但由于返回結果是接口類型,我們并不知道類庫實際選擇的容器類型是什么,有時候我們可能會想要人為指定容器的實際類型,這個需求可通過Collectors.toCollection(Supplier<C> collectionFactory)方法完成。





上述代碼(3)處指定規約結果是ArrayList,而(4)處指定規約結果為HashSet。一切如你所愿。



使用collect()做字符串join

這個肯定是大家喜聞樂見的功能,字符串拼接時使用Collectors.joining()生成的收集器,從此告別for循環。Collectors.joining()方法有三種重寫形式,分別對應三種不同的拼接方式。無需多言,代碼過目難忘。



collect()還可以做更多

除了可以使用Collectors工具類已經封裝好的收集器,我們還可以自定義收集器,或者直接調用collect(Supplier<R> supplier, BiConsumer<R,? super T> accumulator, BiConsumer<R,R> combiner)方法,收集任何形式你想要的信息。不過Collectors工具類應該能滿足我們的絕大部分需求,手動實現之間請先看看文檔。后續有機會分享更多Collectors操作案例

總結

以上是生活随笔為你收集整理的JDK1.8 升级这么久!Stream 流的规约操作有哪些?的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。