[三]java8 函數式編程Stream 概念深刻理解 Stream 運行原理 Stream設計思路

Stream的概念定義

 
官方文檔是永遠的聖經~
 
image_5b7a513e_5740
 
表格內容來自https://docs.oracle.com/javase/8/docs/api/   Package java.util.stream  一節部分原文內容的翻譯
 
int sum = widgets.stream()
.filter(b -> b.getColor() == RED)
.mapToInt(b -> b.getWeight())
.sum();

 

 
流操做被劃分爲中間和終端操做,並組合成流管道。
一條Stream管道由一個源(如一個集合、一個數組、一個生成器函數或一個i/o通道)組成;
而後是零個或更多的中間操做,例如Stream.filter或者 Stream.map
還有一個終端操做,Stream.forEach or Stream.reduce
 
中間操做返回一條新流,他們老是惰性的;
執行諸如filter()之類的中間操做實際上並不會當即執行任何過濾操做,而是建立了一個新流,當遍歷時,它包含與給定謂詞相匹配的初始流的元素。直到管道的終端操做被執行,管道源的遍歷纔會開始
 
終端操做,例如Stream.forEach 和 IntStream.sum,能夠遍歷流以產生結果或反作用。
在執行終端操做以後,流管道被認爲是被消耗掉的,而且不能再被使用;
若是您須要再次遍歷相同的數據源,您必須返回到數據源以得到一條新的stream。
在幾乎全部狀況下,終端操做都很迫切,在返回以前完成了數據源的遍歷和管道的處理。只有終端操做iterator() 和 spliterator() 不是;這些都是做爲一個「逃生艙口」提供的,以便在現有操做不足以完成任務的狀況下啓用任意客戶控制的管道遍歷(我的理解就是若是流不足以提供處理可讓你自行遍歷處理)
 
延遲處理流能夠顯著提升效率;
在像上面的filer-map-sum例子這樣的管道中,過濾、映射和求和能夠被融合到數據的單個傳遞中,而且具備最小的中間狀態。
惰性還容許在沒有必要的狀況下避免檢查全部數據;對於諸如「查找第一個超過1000個字符的字符串」這樣的操做,只須要檢查足夠的字符串,就能夠找到具備所需特徵的字符串,而不須要檢查源的全部字符串。(當輸入流是無限的而不只僅是大的時候,這種行爲就變得更加劇要了。)
 
中間操做被進一步劃分爲無狀態和有狀態操做。
無狀態操做,如filter和map,在處理新元素時不保留之前處理的元素的狀態——每一個元素均可以獨立於其餘元素的操做處理。有狀態的操做,例如distinct和sorted,能夠在處理新元素時從先前看處處理的元素中合併狀態。
 
有狀態操做可能須要在產生結果以前處理整個輸入。
例如,一我的不能從排序流中產生任何結果,直到一我的看到了流的全部元素。
所以,在並行計算下,一些包含有狀態中間操做的管道可能須要對數據進行屢次傳遞,或者可能須要緩衝重要數據。包含徹底無狀態的中間操做的管道能夠在單次傳遞過程當中進行處理,不管是順序的仍是並行的,只有最少的數據緩衝
 
此外,一些操做被認爲是短路操做。一箇中間操做,若是在提供無限流輸入時,它可能會產生一個有限的流,那麼他就是短路的。若是在無限流做爲輸入時,它可能在有限的時間內終止,這個終端操做是短路的。
在管道中進行短路操做是處理無限流在有限時間內正常終止的必要條件,但不是充分條件
這些流的方法是如何實現的?
類StreamSupport提供了許多用於建立流的低級方法,全部這些方法都使用某種形式的Spliterator.
一個Spliterator.是迭代器Iterator的並行版本
它描述了一個(多是無限的)元素集合,支持順序前進、批量遍歷,並將一部分輸入分割成另外一個可並行處理的Spliterator 在最低層,全部的流都由一個spliterator 構造(因此說流就是迭代器的一種高級形式)

在實現Spliterator時,有許多實現選擇,幾乎全部的實現都是在簡單的實現和使用Spliterator流的運行時性能之間進行權衡。建立Spliterator的最簡單、但最不高性能的方法是,使用Spliterators.spliteratorUnknownSize(java.util.Iterator, int).雖然這樣的Spliterator能夠工做,但它極可能提供糟糕的並行性能,由於咱們已經丟失了尺寸信息(底層數據集有多大),而且被限制爲一個簡單的分割算法。

一個高質量的Spliterator將提供平衡的和知道大小的分割,精確的尺寸信息,以及一些可用於實現優化執行的spliterator 或數據的 characteristics (見Spliterator int characteristics()  )

可變數據源的Spliterators有一個額外的挑戰;
綁定到數據的時間,由於數據可能在建立spliterator的時間和執行流管道的時間之間發生變化。理想狀況下,一個流的spliterator 應該報告一個characteristic of IMMUTABLE or CONCURRENT;
若是不是,應該是後期綁定。若是一個源不能直接提供一個推薦的spliterator,它可能會間接地經過Supplier提供一個spliterator,經過接收Supplier做爲參數的stream方法構建一個流
public static <T> Stream<T> stream(Supplier<? extends Spliterator<T>> supplier,
                                   int characteristics,
                                   boolean parallel)

 

只有在流管道的終端操做開始後,才從supplier處獲
 
 
 
image_5b7a513e_622
Stream  文檔概要譯文:
支持順序和並行聚合操做的一組元素序列
除了Stream 還有專門爲原始類型特殊化的IntStream、LongStream和double Stream  全部這些都被稱爲「流」
集合和流,雖然表面上有一些類似性,但有不一樣的設計目的
集合主要關注的是對其元素的有效管理和訪問
相比之下,流並無提供直接訪問或操縱其元素的方法,而是關注於聲明性地描述它們的源和計算操做,這些操做將在該源上進行聚合。
可是,若是所提供的流操做沒有提供所需的功能,那麼 BaseStream.iterator() 和 BaseStream.spliterator() 操做能夠用來執行受控的遍歷
 
示例:
widgets 是 Collection<Widget>
int sum = widgets.stream()
.filter(w -> w.getColor() == RED)
.mapToInt(w -> w.getWeight())
.sum();

 

像上面的「widgets」示例同樣,流管道能夠看做是在流的數據源上進行的查詢。
 
除非源代碼是爲併發修改而顯式設計的(例如ConcurrentHashMap),不然在查詢時 修改流的源 可能致使不可預測或錯誤的行爲。
 
大多數流操做都接受描述用戶指定行爲的參數,好比在上面的例子中傳遞給mapToInt的lambda表達式w-w.getweight()。
爲了保持正確的行爲,這些行爲參數:
        必須是非干擾(也就是它們不修改流源);
        在大多數狀況下,必須是無狀態的(它們的結果不該該依賴於任何在流水線執行過程當中可能發生變化的狀態)
這些參數一般是函數接口的實例,例如Function,通常是lambda表達式或方法引用。除非另有說明,這些參數必須是非空的。
 
一個流應該只運行一次(調用中間操做或結束操做)。這就排除了好比「forked」流,在這些流中,相同的源提供兩個或更多的管道,或者同一流的多個遍歷。
一個流實現可能會拋出IllegalStateException 異常,若是它檢測到流正在被重用。
然而,因爲某些流操做可能返回它們的接收者而不是一個新的stream對象,因此並不能在全部狀況下都檢測到重用。
 
Streams有一個BaseStream.close()方法並實現AutoCloseable,可是幾乎全部的stream實例在使用後實際上並不須要關閉。
一般,只有源是IO通道的流(比Files.lines(Path,Charset))將須要關閉。
大多數流都是由集合、數組或生成函數支持的,這些功能不須要特殊的資源管理。(若是流確實須要關閉,它能夠在try-with-resources語句中聲明爲資源。)
 
流管道能夠按順序或並行執行 ,這種執行模式是流的屬性。
流的類型是建立初始時選擇經過順序或並行操做執行來決定的。(例如,Collections.stream()建立了一個順序流,而Collection.parallelStream()建立了一個並行的流。)
這種執行模式的選擇能夠由BaseStream.sequential() 或BaseStream.parallel()方法修改,而且可使用BaseStream.isParallel() 方法查詢。
 
 

流的基本特色

 
集合是對一組特定類型的元素值序列提供的接口  是數據結構,提供了元素的存取
流也是對一組特定類型元素值序列提供的接口,在於計算,提供了對元素序列的操做計算方式 好比 filter map等
流只能運行一次
流由源 0個或者多箇中間操做以及結束操做組成
流操做的方法基本上是函數式接口的實例
流的中間操做是惰性的並不會當即執行 這更有利於內部迭代的優化
流藉助於它內部迭代特性提供了聲明式的編程方式 更急簡潔
中間操做自己會返回一個流,能夠將多個操做複合疊加,造成一個更大的流水線
流分爲順序和並行兩種方式
 

流與集合主要區別

 
不存儲數據
        流不是存儲元素的數據結構;相反,它經過一個計算操做的管道,從一個數據源,如數據結構、數組、生成器函數或i/o通道中傳遞元素
函數特性
        一個流上的操做產生一個結果,可是不會修改它的源。例如,過濾集合 得到的流會產生一個沒有被過濾元素的新流,而不是從源集合中刪除元素
延遲搜索
        許多流操做,如過濾、映射或重複刪除,均可以延遲實現,從而提供出優化的機會。
        例如,「找到帶有三個連續元音的第一個字符串」不須要檢查全部的輸入字符串。
        流操做分爲中間(流生成)操做和終端(值或反作用生成)操做。許多的中間操做, 如filter,map等,都是延遲執行。
       中間操做老是lazy的。
Stream多是無界的
        雖然集合的大小是有限的,但流不須要。諸如limit(n)或findFirst()這樣的短路操做能夠容許在有限時間內完成無限流的計算。
消耗的
        流的元素只在流的生命週期中訪問一次。就像迭代器同樣,必須生成一個新的流來從新訪問源的相同元素
 
 
能夠把流當作一個高級的迭代器Iterator ,內部有它自身運行邏輯的迭代器
你只須要告訴他你想要作什麼,他本身就會自動的去迭代篩選組織你想要的數據
 
 
 

Stream的生成

 
目前在java中 集合框架與Stream的結合最多
由於Stream 是對數據項的計算,而集合偏偏是用來存儲數據項的數據結構
你固然可使用其餘的數據項表示形式

方法列表

  • collection.stream   parallelStream  實例方法 轉換
  • Arrays.stream(Object[]) 靜態方法 轉換
  • Stream類的靜態工廠方法 好比  Stream.of(Object[]), IntStream.range(int, int), Stream.iterate(Object, UnaryOperator)   Stream.generate   
  • BufferedReader.lines(); 文件行
  • Files類的獲取文件路徑列表: find(), lines(), list(), walk();
  • Random.ints()  隨機數流
  • JDK中的許多其餘流載方法,包括BitSet.stream(), Pattern.splitAsStream(java.lang.CharSequence), and JarFile.stream().
 

經常使用建立方法

 
Stream.of( )      Stream類靜態方法     
轉換XXX爲Stream
 
image_5b7a513e_31ea
 
Collection stream   集合轉換爲Stream
特   別   注   意:這是一個default方法,也就意味着若是沒有特別處理,全部Collection子類都具備
image_5b7a513e_bb5
 
Arrays.stream();     數組轉換爲Stream
image_5b7a513e_139b
 
 
Stream.iterate   Stream類靜態方法
迭代器的形式,建立一個數據流
 
image_5b7a513e_7609
 
好比
image_5b7a513e_7292
image_5b7a513e_1aed
Stream.generate 
image_5b7a513f_6bac
 
好比
image_5b7a513f_70eb
image_5b7a513f_3be3
 
 
 

Stream類體系結構與流水線設計思路

image_5b7a513f_4750
BaseStream規定了流的基本接口
Stream中定義了map、filter、flatmap等用戶關注的經常使用操做;
Int~ Long~ Double~是針對於基本類型的特化 方法與Stream中大體對應,固然也有一些差異
BaseStream Stream IntStream LongStream DoubleStream 組建了Java的流體系根基
他們都只是接口
image_5b7a513f_2ac0
PipelineHelper主要用於Stream執行過程當中相關結構的構建
ReferencePipeline  和 AbstractPipeline 完成了Stream的主要實現
AbstractPipeline類實現了全部的 Stream的中間操做和最終操做
[Int | Long | Double] pipeline 同理相似ReferencePipeline只不過是針對基本類型

image_5b7a513f_4f3b

 
 
 
image_5b7a513f_159a
 
Head、StatelessOp、StatefulOp爲ReferencePipeline中的內部類
[Int | Long | Double]Pipeline 內部也都是定義了這三個內部類
 
說到這咱們已經能夠清晰地知道Stream的實現類
 
 

流的族譜

 

 
 
回頭看一下獲取Stream的方式
好比Collection中調用StreamSupport.stream 返回的是ReferencePipline.Head
image_5b7a513f_999
 
再好比 Stream的of方法 也仍是調用了StreamSupport.stream方法 他返回的仍是ReferencePipline.Head
image_5b7a513f_59f9
 
你會發現流的生成轉換建立都是使用StreamSupport
StreamSupport 是用於建立和操縱Stream的低級工具方法
除了構造方法,每一個方法都是返回他們對應的Head
ReferencePipeline.Head / DoublePipeline.Head / IntPipeline.Head /  LongPipeline.Head 
 
image_5b7a513f_ac2
 
Stream的操做通常都有三部分構成
數據源
操做( filter map.....)
回調方法(Lambda匿名函數 方法引用)
 

 Stream  使用stage來對操做進行抽象描述

 
 
能夠看下Head  StatefulOp  StatelessOp 的源碼註釋就能夠知道了 .分別是 :
Source stage of a ReferencePipeline. 
Base class for a stateful intermediate stage of a Stream.
Base class for a stateless intermediate stage of a Stream.
原始類型特化出來的也是同樣
咱們知道Head  StatefulOp  StatelessOp是     ReferencePipeline類型的 ([Int | Long | Double]Pipeline).他們也都是Stream
因此說每一個操做其實也都是Stream
如今也就能夠明白爲何建立轉換生成的流都是Head 了 由於它用來抽象描述 源階段也就是初始階段
 
再回頭看下AbstractPipeline的概念
「管道」類的抽象基類,它們是流接口及其原始專門化的核心實現。管理管道的建設和評估審查
AbstractPipeline表示一個流管道的初始部分,封裝了一個流源和零個或多箇中間操做。
單獨的AbstractPipeline對象一般用來表示階段,其中每一個階段描述的是流源或中間操做。
 
一個具體的中間階段一般是由AbstractPipeline 構建的--一個特定於操做的具體類,他繼承於AbstractPipeline的子類的好比IntPipeline
說白了也就是上面說到過的Head  StatefulOp  StatelessOp  他們自己也是AbstractPipeline類型的
AbstractPipeline包含了評估管道的大部分機制,並實現了操做所使用的方法;
特定類型的類添加了助手方法,用於處理將結果收集到適當的特定類型的容器中。
 

那麼AbstractPipeline 是怎麼構造管道的呢?

 
先說Head  這是建立生成流的時候返回的對象
image_5b7a513f_451a
能夠看得出來 Head的構造也仍是調用的AbstractPipeline
仔細看下他的屬性域
private final AbstractPipeline sourceStage;  //反向連接管道鏈的head,也就是說每一個管道節點都有一個頭
private final AbstractPipeline previousStage;//指向上一個
private AbstractPipeline nextStage;//指向下一個
赤裸裸的雙向鏈表
 
 
image_5b7a513f_58ab
首先是構造,最終調用AbstractPipeline的構造方法
而後當前節點做爲源
上一個爲null  下一個爲null
image_5b7a513f_1c1e
 
而後再看下filter和map方法
image_5b7a5140_4583
Stream中將操做抽象化爲stage 每一個stage 也就是一個AbstractPipeline
每一個stage 至關於一個雙向鏈表的節點  ,每一個節點都保存Head而後保存着上一個和下一個
這個雙向鏈表就構成了整個流水線
(上面的圖看起來next一直是null 是在每一個處理後的this裏面的previousStage裏面 上一個的next是當前)
 
 
image_5b7a5140_1a8b
 
每一個操做向操做同樣組合成爲雙向鏈表
鏈表將每一個操做流水線化,可是每一個操做具體的行爲是什麼?
那麼,每一個操做的具體細節又是什麼樣子的呢?
 

Sink是什麼?

sink就是每一個操做具體的行爲操做,也能夠叫作回調
 
sink是Consumer的擴展,用於流管道中的多個操做階段中進行數據的監管
經過額外的方法來管理大小信息 流控等
調用accept前須要調用begin通知數據到來,數據發送完成後須要調用end,再次調用accept前必須再次調用begin 
一個sink有兩種狀態,初始/激活
開始時是初始狀態,begin 激活 ,  end使之回到初始狀態,能夠重複利用 
accept只能在激活狀態使用
 
Sink用於協調相鄰的stage之間的數據調用
經過begin end accept方法 以及cancellationRequested短路標誌位來控制處理流程,對數據進行管控
 
 
再次回頭看看filter的代碼(理解這個過程須要瞭解閉包 回調的概念)
image_5b7a5140_633a
 
 
 
每一個操做 statelessOp 或者statefulOp 都是一個AbstractPipeline  也都是stream    也就是流水線的一個抽象stage階段
AbstractPipeline 他提供了一個opWrapSink方法
image_5b7a5140_1d3b
statelessOp 或者statefulOp   重寫了這個方法
返回了一個Sink對象    (Chained[Reference | Int | Long | Double] 其實都是Sink)
這個對象的accept方法  調用 參數 predicate的test方法  其實也就是調用Lambda 行爲化的參數  
這一步至關於封裝了當前stage的回調函數
而且他還調用了downstream.accept()方法
 

downstream  是什麼?

image_5b7a5140_5b7b
他就是你傳遞進去的那個參數 sink
也就是說 statelessOp 或者statefulOp 是流水線 stage的抽象
他有一個opWrapSink()方法,他其中一個參數是Sink
這個方法自己返回一個Sink  sink的accept方法封裝了回調函數 也就是當前操做階段的行爲
而後他還會調用參數sink的accept方法
試想,若是傳遞過來的是下一個操做階段的sink呢?
image_5b7a5140_22a4
思考下上面的這個調用會有什麼效果
AbstractPipeline中有一個方法
wrapSink來自於PipelineHelper
image_5b7a5140_335c
從最後一個開始,按照深度進行
如今流水線上從開始到結束的全部的操做都被包裝到了一個Sink裏  
 

Stream運行流程原理

 
Stream體系是一組接口家族,AbstractPipeline 是接口的實現,PipelineHelper 是管道的輔助類,StreamSupport是流的低級工具類
 
使用stage來抽象流水線上的每一個操做
其實每一個stage就是一個stream 也就是AbstractPipeline幾個子類的  內部子類  Head StatelessOp statefulOp
StreamSupport用於建立生成Stream 對應的是Head類
其餘的中間操做分爲有狀態和無狀態的,中間操做經過方法好比 filter map 等返回的是StatelessOp  或者 statefulOp 
多個stage組合稱爲雙向鏈表的形式 從而成了整個流水線
 
有了流水線,相鄰兩個操做階段之間如何協調運算?
因而又有了sink的概念,又來協調相鄰的stage之間計算運行
他的模式是begin  accept end 還有短路標記
他的accept就是封裝了回調方法
 
因此說每一個操做stage, StatelessOp  或者 statefulOp中又封裝了Sink
經過AbstractPipeline提供的opWrapSink方法能夠獲取這個sink
調用這個sink的accept方法就能夠調用當前操做的方法
 
那麼如何串聯起來呢?關鍵點在於opWrapSink方法 ,他接收一個Sink做爲參數
在調用accept方法中  能夠調用這個入參sink的accept方法
這樣子從當前就能調用下一個,也就是說有了推進的動做
那麼只須要找到開始,每一個處理了以後都推進下一個,就順序完成了所欲的操做了
 
注意上面說的操做都是中間操做,中間操做纔會產生操做階段  終端操做不會增長stage的個數了
 
 
 

Stream預置操做

 
中間操做  filter() flatMap() limit() map()  concat() distinct() peek() skip() sorted() parallel() sequential() unordered() 
flatMapTo[Double | Int | Long]  mapTo[ Double | Int | Long ]
結束操做 allMatch() anyMatch() collect() count() findAny() findFirst() forEach() forEachOrdered() max() min() noneMatch() reduce() toArray() 
 
操做的參數基本上是函數式接口的實例---->也就是Lambda匿名函數   方法引用
因此說想要使用Stream預置的函數,只須要了解清楚對應的函數式接口便可
image_5b7a5140_c2c
 
 

Stream 主要有四類接口

 
流到流之間的轉換
filter(過濾), map(映射轉換), mapTo[Int|Long|Double] (到基本類型流的轉換),
flatMap(流展開合併),flatMapTo[Int|Long|Double],
sorted(排序),distinct(不重複值),peek(執行某種操做,流不變,可用於調試),
limit(限制到指定元素數量), skip(跳過若干元素) 
流到終值的轉換
toArray(轉爲數組),reduce(推導結果),collect(聚合結果),
min(最小值), max(最大值), count (元素個數),
anyMatch (任一匹配), allMatch(全部都匹配), noneMatch(一個都不匹配)
findFirst(選擇首元素),findAny(任選一元素)
直接遍歷
forEach (不保證順序遍歷,好比並行流), forEachOrdered(順序遍歷)
構造流
empty (構造空流),of (單個元素的流及多元素順序流)
iterate (無限長度的有序順序流),generate (將數據提供器轉換成無限非有序的順序流),
concat (流的鏈接), Builder (用於構造流的Builder對象)

 
 

Stream經常使用API

 
filter 條件篩選   boolean test(T t); 
image_5b7a5141_3232
 
image_5b7a5141_3744
 
map   數據轉換    R apply(T t); 新的值替換Stream中的值
mapTo[Int | Long | Double] 相似
 
 
image_5b7a5141_2110
image_5b7a5141_1d56
 
 
flatMap  R apply(T t); 同map可是是多個流轉換爲一個流 返回值限定爲Stream
其餘flatMapTo[Int | Long | Double] 相似
image_5b7a5141_2110[1]
image_5b7a5141_4d71
 
 
 
生成流 iterate()
public static<T> Stream<T> iterate(final T seed, final UnaryOperator<T> f)
image_5b7a5141_1889
UnaryOperator  是 Function的一元運算形式
操做單個的操做數,產生的結果同操做數類型,是Function轉換數據--針對操做數和結果類型一致的一種特殊類型
好比 x->x+2  就是如此,操做數爲x   轉換爲x+2

Stream.iterate(0, i -> i + 2)java

 
生成流 generate()
public static<T> Stream<T> generate(Supplier<T> s)
image_5b7a5141_674
image_5b7a5141_6ac9
 
 

reduce  歸約  原理

一個歸約操做(也稱爲摺疊)接受一系列的輸入元素,並經過重複應用組合操做將它們組合成一個簡單的結果
例如查找一組數字的總和或最大值,或者將元素累積到一個列表中。
流的類中有多種形式的通用歸約操做,稱爲reduce()和collect(),以及多個專門化的簡化形式,如sum()、max()或count()。
固然,這樣的操做能夠很容易地實現爲簡單的順序循環,以下所示:
int sum = 0;
for (int x : numbers) {
sum += x;
}

 

 
然而,咱們有充分的理由傾向於減小操做,而不是像上面這樣的累加運算。
它不只是一個「更抽象的」——它在流上做爲一個總體而不是單獨的元素來運行——並且一個適當構造的reduce操做本質上是可並行的,只要用於處理元素的函數(s)是結合的和無狀態的。舉個例子,給定一個數字流,咱們想要找到和,咱們能夠寫:
    int sum = numbers.stream().reduce(0, (x,y) -> x+y);
或者
    int sum = numbers.stream().reduce(0, Integer::sum);
這些歸約操做幾乎不須要修改就能夠並行運行
    int sum = numbers.parallelStream().reduce(0, Integer::sum);
 
若是一個操做符或函數 op  知足    (a op b) op c == a op (b op c) ,那麼他是結合的
結合性對於並行結算很是重要
好比
     a op b op c op d == (a op b) op (c op d)     就能夠並行計算  (a op b)    (c op d)   而後再處理他們
 
 
他是一個結束操做
 
三個重載版本
image_5b7a5141_2077
 
image_5b7a5141_5097 是Function的雙參數版本 接受 T  U 轉換爲R
image_5b7a5141_2129 是BiFunction的特殊化形式,兩個輸入一個輸出,三個參數類型相同
 
Optional<T> reduce(BinaryOperator<T> accumulator)
參數accumulator:  累計計算器——結合兩個值的結合性、非干擾、無狀態函數
由於沒有初始值 因此返回值爲 Optional<T>
計算1+2+3+4+5
image_5b7a5141_4f52
 
至關於
boolean foundAny = false;
T result = null;
for (T element : this stream) {
if (!foundAny) {
foundAny = true;
result = element;
}
else
result = accumulator.apply(result, element);
}
return foundAny ? Optional.of(result) : Optional.empty();

 

 

若是不進入循環,那麼foundAny就是false 直接返回
不然第一次循環給result賦值,此後foundAny 爲true 
result做爲一個操做數調用accumulator.apply()
 
BinaryOperator 意味着兩個操做數一個結果數 類型同樣 這可不只僅用於累加
image_5b7a5141_42bd
image_5b7a5141_1c10
還能夠用來合併字符串 多種形式
image_5b7a5141_bd5
image_5b7a5141_3c36
image_5b7a5141_1334
 
 

T reduce(T identity, BinaryOperator<T> accumulator);
identity 能夠理解爲初始值, accumulator 同上
image_5b7a5141_6049
image_5b7a5141_238a
你會發現對於上面的例子,可使用Integer::sum 替代其中的lambda
image_5b7a5141_383c
至關於
T result = identity;
for (T element : this stream)
result = accumulator.apply(result, element)
return result;

 

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

 

至關於:
U result = identity;
for (T element : this stream)
result = accumulator.apply(result, element)
return result;

 

看得出來和T reduce(T identity, BinaryOperator<T> accumulator);  幾乎是同樣的
只不過Stream是T 返回是U   可能同樣,可能不同了
若是類型相同的話就是徹底同樣的了
第三個參數用於在並行計算下 合併各個線程的計算結果
因此要求
     combiner.apply(u, accumulator.apply(identity, t)) == accumulator.apply(u, t)
 
image_5b7a5141_4fa
 
image_5b7a5141_4bb1
結果不一樣  是由於  ((((5+1)+2)+3)+4)+5   和   (5+1)+ (5+2)+ (5+3)+ (5+4)+ (5+5)  運算結果不相同
 
image_5b7a5141_18ac
 
image_5b7a5141_69dd
此處  挨個比較找到最大,和 使用8和每一個數字比較而後在統一比較 的結果是相同的
相關文章
相關標籤/搜索