Java8之Stream流(三)縮減操作

Java8之Stream流(一)基礎體驗
Java8之Stream流(二)關鍵知識點
Java8之Stream流(四)并行流
Java8之Stream流(五)映射流
Java8之Stream流(六)收集
Java8之Stream流(七)流與迭代器

和前面兩篇文章一起服用,效果會更佳。通過對流API的基礎體驗Demo和關鍵知識點的講解 ,相信大家對流API都有一定的認識了,但是流API強大的功能,可不僅僅像前面兩篇文章中說的那樣簡單,大家應該注意到,在第二篇中,我對Stream接口進行介紹的時候,并沒有把他的全部方法都進行了解析說明。沒錯,從這一篇開始,那些還沒有講解的方法,很可能就開始變成我們的主角了,大家從題目上面應該知道了,本期我們要講的是流API的縮減操作。

何為縮減操作?

我們先考慮一下min()和max(),這兩個方法我們在第一篇和第二篇中均有提到,其中min()是返回流中的最小值,而max()返回流中最大值,前提是他們存在。他們之間的特點是什么?①都返回了一個值②由一可知,他們是終端操作。如果我們用流API的術語來形容前面這兩種特性的結合體的話,它們代表了縮減操作。因為每個縮減操作都把一個流縮減為一個值,好比最大值,最小值。當然流API,把min()和max(),count()這些操作稱為特例縮減。即然說到了特例,肯定就有泛化這種概念了,他就是reduce()方法了,其實第二篇當中,他已經出現過了,只是當時我沒有去強調他。

public interface Stream<T> extends BaseStream<T, Stream<T>> {
//、、、忽略其他無關緊要的元素
T reduce(T identity, BinaryOperator<T> accumulator);
Optional<T> reduce(BinaryOperator<T> accumulator);
<U> U reduce(U identity,
          BiFunction<U, ? super T, U> accumulator,
          BinaryOperator<U> combiner);
}

Stream接口定義了三個版本的reduce(),我們先使用前面兩個,

T reduce(T identity, BinaryOperator<T> accumulator);//1
Optional<T> reduce(BinaryOperator<T> accumulator);//2

第一個版本返回的是一個T類型的對象,T代表的是流中的元素類型!第二個版本是返回一個Optional類型對象。對于這兩種形式,accumulator是一個操作兩個值并得到結果的函數。在第一個版本當中,identity是這樣一個值,對于涉及identity和流中任意的累積操作,得到的結果就是元素自身,沒有任何改變。比如,如果是加法,他就是0,如果是乘法他就是1。

其中的accumulator是一個BinaryOperator<T>的類型,他是java.util.function包中聲明的函數式接口,它擴展了BiFunction函數式接口.

@FunctionalInterface
public interface BinaryOperator<T> extends BiFunction<T,T,T> {
}

@FunctionalInterface
public interface BiFunction<T, U, R> {
   R apply(T t, U u);//notice
}

BiFunction接口中的apply()方法的原型在//notice。其中R指定了結果的類型,T,U分別是第一參數的類型和第二個參數的類型,因此apply()對他的兩個操作數(t,u)應用到同一個函數上,并返回結果,而對BinaryOperator<T>來說,他在擴展 BiFunction時,指定了所有的類型參數都是相同的T,因此對于BinaryOperator<T>函數式接口的apply來說,他也就變成了 T apply(T t, T u),此外,還有一個需要注意的地方是,在應用reduce()時,apply()的第一個參數t,包含的是一個結果,u包含的是下一個元素。在第一次調用時,將取決于使用reduce()的版本,t可能是單位值,或者是前一個元素。

縮減操作的三個約束

  • 無狀態
  • 不干預
  • 關聯性

無狀態,這里可不是LOL的那個無狀態,畢竟他退役了。相信讀過第二篇文章的同學已經很容易理解了,簡單來說無狀態就是每個元素都被單獨地處理,他和流中的其它元素是沒有任何依賴關系的。不干預是指操作數不會改變數據源。最后,操作必須具有關聯性,這里的關聯性是指標準的數學含義,即,給定一個關聯運算符,在一系列操作中使用該運算符,先處理哪一對操作數是無關緊要的。比如,(1 * 2) * 3 <===> 1 * (2 * 3)。其中關聯性,在并行流中,是至關重要的。下面我用一個簡單的例子帶著大家實戰一下泛化縮減操作reduce()的使用。

public class Main {

    public static void main(String[] args) {
        learnStream();
    }


    private static void learnStream() {
        List<Integer> lists = new ArrayList<>();
        lists.add(1);
        lists.add(2);
        lists.add(3);
        lists.add(4);
        lists.add(5);
        lists.add(6);

        Optional<Integer> sum = lists.stream().reduce((a, b) -> a + b);
        if (sum.isPresent()) System.out.println("list的總和為:" + sum.get());//21
        //<====> lists.stream().reduce((a, b) -> a + b).ifPresent(System.out::println);

        Integer sum2 = lists.stream().reduce(0, (a, b) -> a + b);//21
        System.out.println("list的總和為:" + sum2);

        Optional<Integer> product = lists.stream().reduce((a, b) -> a * b);
        if (product.isPresent()) System.out.println("list的積為:" + product.get());//720

        Integer product2 = lists.stream().reduce(1, (a, b) -> a * b);
        System.out.println("list的積為:" + product2);//720
    }
}

這個Demo主要是計算了一個list里面的總和,積的操作,大家可以和傳統的算總和,積的方法進行對照,比一比衡量一下就有自己的答案了。但是如果你以為流API僅此而已,那你就錯了。越是后面的東西,就越裝B,我在剛知道他們的時候,反正是被嚇了一跳的,但這些都是后話了,現在我們來詳解一下Demo,并給出擴展的方向:我們這個例子主要是用了lambda表達式對list進行了求和,求積,對于第一個版本為說,求和的時候,identity的值為0,求積的時候它的值為1,強烈建議你們自己感受一下identity的變化對整個結果的變化產生什么 的影響,改變一下identity的值,再運行一下,你就有結果了,另一個擴展點是:

 Integer product3 = lists.stream().reduce(1, (a, b) -> {
            if (b % 2 == 0) return a * b; else return a;//這里你可以為所欲為!
 });
 System.out.println("list的偶數的積為:" + product3);//48

小結一下

對于流的縮減操作來說,主要要知道,他只返回一個值,并且它是一個終端操作,然后還有的就是要知道縮減操作的三個約束了,其實最重要的就是無狀態性和關聯性了.這一小節要說的,也就這么多了,應該很容易就把他收到自己的技能樹上面了。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,565評論 6 539
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,115評論 3 423
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,577評論 0 382
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,514評論 1 316
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,234評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,621評論 1 326
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,641評論 3 444
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,822評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,380評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,128評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,319評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,879評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,548評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,970評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,229評論 1 291
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,048評論 3 397
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,285評論 2 376

推薦閱讀更多精彩內容

  • Java流庫(java.util.stream) 流提供了一種讓我們可以在比集合更高的概念級別上指定計算的數據視圖...
    thorhill閱讀 4,864評論 0 4
  • 原文地址 http://blog.csdn.net/myherux/article/details/7185511...
    Vissioon閱讀 691評論 0 0
  • Java8 in action 沒有共享的可變數據,將方法和函數即代碼傳遞給其他方法的能力就是我們平常所說的函數式...
    鐵牛很鐵閱讀 1,253評論 1 2
  • 參考書籍:《Java 8函數式編程》 一. 四種最基本的函數式接口 使用Stream類進行流操作之前,先了解一下四...
    納米君閱讀 6,715評論 3 8
  • 試下速寫,15分鐘搞定一張。 先用黑色中性簽字筆直接勾線,確定大致輪廓。 頭發分組,衣服線描。 一綹一綹畫頭發,注...
    手繪者煥新閱讀 632評論 4 9