最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

解析Java8 Stream原理

 更新時(shí)間:2021年06月17日 11:13:44   作者:vivo互聯(lián)網(wǎng)技術(shù)  
說(shuō)起 Java 8,我們知道 Java 8 大改動(dòng)之一就是增加函數(shù)式編程,而 Stream API 便是函數(shù)編程的主角,Stream API 是一種流式的處理數(shù)據(jù)風(fēng)格,也就是將要處理的數(shù)據(jù)當(dāng)作流,在管道中進(jìn)行傳輸,并在管道中的每個(gè)節(jié)點(diǎn)對(duì)數(shù)據(jù)進(jìn)行處理,如過(guò)濾、排序、轉(zhuǎn)換等

一、前言

首先我們先看一個(gè)使用Stream API的示例,具體代碼如下:

這是個(gè)很簡(jiǎn)單的一個(gè)Stream使用例子,我們過(guò)濾掉空字符串后,轉(zhuǎn)成int類(lèi)型并計(jì)算出最大值,這其中包括了三個(gè)操作:filter、mapToInt、sum。相信大多數(shù)人再剛使用Stream API的時(shí)候都會(huì)有個(gè)疑問(wèn),Stream是指怎么實(shí)現(xiàn)的,是每一次函數(shù)調(diào)用就執(zhí)行一次迭代嗎?答案肯定是否,因?yàn)槿绻娴氖敲恳淮魏瘮?shù)調(diào)用就執(zhí)行一次迭代,這個(gè)效率是很難接受的,Stream也不會(huì)那么受歡迎。

其實(shí)Stream內(nèi)部是通過(guò)流水線(Pipeline)的方式來(lái)實(shí)現(xiàn)的,基本思想是在迭代的時(shí)候順著流水線盡可能的執(zhí)行更多的操作,從而避免多次迭代。為了對(duì)Stream的操作有更清晰的認(rèn)識(shí),我們匯總了Stream的所有操作。

從上表可以看出Stream將所有操作分為兩類(lèi):中間操作和終止操作。其中中間操作分為無(wú)狀態(tài)和有狀態(tài),終止操作分為非短路操作和短路操作,下面是針對(duì)這幾個(gè)操作的含義說(shuō)明:

1、中間操作:中間操作只是一種標(biāo)記,只有結(jié)束操作才會(huì)觸發(fā)實(shí)際計(jì)算

  • 無(wú)狀態(tài):指元素的處理不受前面元素的影響;
  • 有狀態(tài):有狀態(tài)的中間操作必須等到所有元素處理之后才知道最終結(jié)果,比如排序是有狀態(tài)操作,在讀取所有元素之前并不能確定排序結(jié)果。

2、終止操作:顧名思義,就是得出最后計(jì)算結(jié)果的操作

  • 短路操作:指不用處理全部元素就可以返回結(jié)果;
  • 非短路操作:指必須處理所有元素才能得到最終結(jié)果。

二、Stream流水線解決方案

通過(guò)上面的介紹,我們了解到Stream在執(zhí)行中間操作時(shí)僅僅是記錄,當(dāng)用戶調(diào)用終止操作時(shí),會(huì)在一個(gè)迭代里將已經(jīng)記錄的操作順著流水線全部執(zhí)行掉。沿著這個(gè)思路,有幾個(gè)問(wèn)題需要解決:

  • 用戶的操作如何記錄?
  • 操作如何疊加?
  • 疊加之后的操作如何執(zhí)行?

2.1、操作如何記錄

圖1-1

關(guān)于操作如何記錄,在JDK源碼注釋中多次用(操作)stage來(lái)標(biāo)識(shí)用戶的每一次操作,而通常情況下Stream的操作又需要一個(gè)回調(diào)函數(shù),所以一個(gè)完整的操作是由數(shù)據(jù)來(lái)源、操作、回調(diào)函數(shù)組成的三元組來(lái)表示。而在具體實(shí)現(xiàn)中,使用實(shí)例化的ReferencePipeline來(lái)表示,即圖1-1中的Head、StatelessOp、StatefulOp的實(shí)例。接下來(lái)我們來(lái)看下Stream幾個(gè)常用方法的源碼。

code2 Collection.Stream()

code3StreamSupport.stream()

code4 ReferencePipeline.map()

從上面源碼中可以看出來(lái),我們調(diào)用stream()方法時(shí)最終會(huì)創(chuàng)建一個(gè)Head實(shí)例來(lái)表示流操作的頭,當(dāng)調(diào)用map()方法時(shí)則會(huì)創(chuàng)建無(wú)狀態(tài)的中間操作實(shí)例StatelessOp,同樣調(diào)用其他操作對(duì)應(yīng)的方法也會(huì)生成一個(gè)ReferencePipeline實(shí)例,在這里就不一一列舉。在用戶調(diào)用一系列操作后,最終會(huì)形成一個(gè)雙向鏈表,如下圖所示:

圖1-2

2.2、操作如何疊加

上面我們說(shuō)明了Stream是通過(guò)stage記錄操作,但stage只保存當(dāng)前操作,它并不知道下個(gè)stage如何操作,需要什么操作。所以要執(zhí)行的話還需要某種協(xié)議將各個(gè)stage關(guān)聯(lián)起來(lái)。jdk中就是使用Slink接口來(lái)實(shí)現(xiàn)的,Slink接口定義begin()、end()、cancellationRequested()、accept()四個(gè)方法,如下表所示。

往回看code3 ReferencePipeline.map()的方法,我們會(huì)發(fā)現(xiàn)我們?cè)趧?chuàng)建一個(gè)ReferencePipeline實(shí)例的時(shí)候,需要重寫(xiě)opWrapSink方法來(lái)生成對(duì)應(yīng)Sink實(shí)例。而且通過(guò)閱讀源碼會(huì)發(fā)現(xiàn)常用的操作都會(huì)創(chuàng)建一個(gè)ChainedReference實(shí)例。我們可以看下code5 ChainedReference抽象類(lèi)的源碼實(shí)現(xiàn),因?yàn)镃hainedReference只是個(gè)抽象實(shí)現(xiàn),不攜帶具體操作的特性,所以是更能體現(xiàn)作者的設(shè)計(jì)理念。

通過(guò)查看源碼可以發(fā)現(xiàn)ChainedReference會(huì)持有下一個(gè)操作的Slink,并在調(diào)用begin、end、cancellationRequested方法會(huì)調(diào)用下一個(gè)操作的Slink的相應(yīng)方法,以此來(lái)達(dá)到疊加的效果。

code5ChainedReference

2.3、疊加之后的操作如何執(zhí)行

Sink完美封裝了Stream每一步操作,并給出了[處理->轉(zhuǎn)發(fā)]的模式來(lái)疊加操作。這一連串的齒輪已經(jīng)咬合,就差最后一步撥動(dòng)齒輪啟動(dòng)執(zhí)行。是什么啟動(dòng)這一連串的操作呢?也許你已經(jīng)想到了啟動(dòng)的原始動(dòng)力就是結(jié)束操作(Terminal Operation),一旦調(diào)用某個(gè)結(jié)束操作,就會(huì)觸發(fā)整個(gè)流水線的執(zhí)行。

結(jié)束操作之后不能再有別的操作,所以結(jié)束操作不會(huì)創(chuàng)建新的流水線階段(Stage),直觀的說(shuō)就是流水線的鏈表不會(huì)在往后延伸了。結(jié)束操作會(huì)創(chuàng)建一個(gè)包裝了自己操作的Sink,這也是流水線中最后一個(gè)Sink,這個(gè)Sink只需要處理數(shù)據(jù)而不需要將結(jié)果傳遞給下游的Sink(因?yàn)闆](méi)有下游)。對(duì)于Sink的[處理->轉(zhuǎn)發(fā)]模型,結(jié)束操作的Sink就是調(diào)用鏈的出口。

我們?cè)賮?lái)考察一下上游的Sink是如何找到下游Sink的。一種可選的方案是在PipelineHelper中設(shè)置一個(gè)Sink字段,在流水線中找到下游Stage并訪問(wèn)Sink字段即可。但Stream類(lèi)庫(kù)的設(shè)計(jì)者沒(méi)有這么做,而是設(shè)置了一個(gè)Sink AbstractPipeline.opWrapSink(int flags, Sink downstream)方法來(lái)得到Sink,該方法的作用是返回一個(gè)新的包含了當(dāng)前Stage代表的操作以及能夠?qū)⒔Y(jié)果傳遞給downstream的Sink對(duì)象。為什么要產(chǎn)生一個(gè)新對(duì)象而不是返回一個(gè)Sink字段?這是因?yàn)槭褂胦pWrapSink()可以將當(dāng)前操作與下游Sink(上文中的downstream參數(shù))結(jié)合成新Sink。試想只要從流水線的最后一個(gè)Stage開(kāi)始,不斷調(diào)用上一個(gè)Stage的opWrapSink()方法直到最開(kāi)始(不包括stage0,因?yàn)閟tage0代表數(shù)據(jù)源,不包含操作),就可以得到一個(gè)代表了流水線上所有操作的Sink,用代碼表示就是這樣:

code6AbstractPipeline.wrapSink

現(xiàn)在流水線上從開(kāi)始到結(jié)束的所有的操作都被包裝到了一個(gè)Sink里,執(zhí)行這個(gè)Sink就相當(dāng)于執(zhí)行整個(gè)流水線,執(zhí)行Sink的代碼如下:

code7AbstractPipeline.copyInto

上述代碼首先調(diào)用wrappedSink.begin()方法告訴Sink數(shù)據(jù)即將到來(lái),然后調(diào)用spliterator.forEachRemaining()方法對(duì)數(shù)據(jù)進(jìn)行迭代,最后調(diào)用wrappedSink.end()方法通知Sink數(shù)據(jù)處理結(jié)束。邏輯如此清晰。

以上就是解析Java8 Stream原理的詳細(xì)內(nèi)容,更多關(guān)于Java8 Stream原理的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 一文詳解SpringBoot?Redis多數(shù)據(jù)源配置

    一文詳解SpringBoot?Redis多數(shù)據(jù)源配置

    Spring?Boot默認(rèn)只允許一種?Redis?連接池配置,且配置受限于?Lettuce?包,不夠靈活,所以本文將為大家介紹如何自定義Redis配置方案實(shí)現(xiàn)多數(shù)據(jù)源支持,需要的可以參考下
    2024-11-11
  • IDEA如何配置本地tomcat啟動(dòng)項(xiàng)目

    IDEA如何配置本地tomcat啟動(dòng)項(xiàng)目

    這篇文章主要介紹了IDEA如何配置本地tomcat啟動(dòng)項(xiàng)目問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-12-12
  • java如何接收和發(fā)送ASCII數(shù)據(jù)

    java如何接收和發(fā)送ASCII數(shù)據(jù)

    這篇文章主要介紹了java如何接收和發(fā)送ASCII數(shù)據(jù)問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-09-09
  • Java輸入輸出語(yǔ)句舉例詳解(通俗易懂!)

    Java輸入輸出語(yǔ)句舉例詳解(通俗易懂!)

    這篇文章主要給大家介紹了關(guān)于Java輸入輸出語(yǔ)句的相關(guān)資料,作為一種常用的編程語(yǔ)言,Java提供了多種輸入輸出的方式,用于與用戶進(jìn)行數(shù)據(jù)交互或處理文件數(shù)據(jù),需要的朋友可以參考下
    2023-10-10
  • 使用Java進(jìn)行FreeMarker的web模板開(kāi)發(fā)的基礎(chǔ)教程

    使用Java進(jìn)行FreeMarker的web模板開(kāi)發(fā)的基礎(chǔ)教程

    這篇文章主要介紹了使用Java進(jìn)行FreeMarker模板引擎開(kāi)發(fā)的基礎(chǔ)教程,文中針對(duì)FreeMarker的網(wǎng)頁(yè)標(biāo)簽用法給出了一些例子,需要的朋友可以參考下
    2016-03-03
  • Spring框架依賴注入方法示例

    Spring框架依賴注入方法示例

    這篇文章主要介紹了Spring框架依賴注入方法示例,分享了三種方法示例,具有一定參考價(jià)值,需要的朋友可以了解下。
    2017-11-11
  • java使用泛型實(shí)現(xiàn)棧結(jié)構(gòu)示例分享

    java使用泛型實(shí)現(xiàn)棧結(jié)構(gòu)示例分享

    泛型是Java SE5.0的重要特性,使用泛型編程可以使代碼獲得最大的重用。由于在使用泛型時(shí)要指明泛型的具體類(lèi)型,這樣就避免了類(lèi)型轉(zhuǎn)換。本實(shí)例將使用泛型來(lái)實(shí)現(xiàn)一個(gè)棧結(jié)構(gòu),并對(duì)其進(jìn)行測(cè)試
    2014-03-03
  • Java結(jié)構(gòu)型設(shè)計(jì)模式之組合模式詳解

    Java結(jié)構(gòu)型設(shè)計(jì)模式之組合模式詳解

    組合模式,又叫部分整體模式,它創(chuàng)建了對(duì)象組的數(shù)據(jù)結(jié)構(gòu)組合模式使得用戶對(duì)單個(gè)對(duì)象和組合對(duì)象的訪問(wèn)具有一致性。本文將通過(guò)示例為大家詳細(xì)介紹一下組合模式,需要的可以參考一下
    2022-09-09
  • Java設(shè)置Excel數(shù)據(jù)驗(yàn)證的示例代碼

    Java設(shè)置Excel數(shù)據(jù)驗(yàn)證的示例代碼

    數(shù)據(jù)驗(yàn)證是Excel 2013版本中,數(shù)據(jù)功能組下面的一個(gè)功能。本文將通過(guò)Java程序代碼演示數(shù)據(jù)驗(yàn)證的設(shè)置方法及結(jié)果,感興趣的可以了解一下
    2022-05-05
  • Spring Boot使用Value注解給靜態(tài)變量賦值的方法

    Spring Boot使用Value注解給靜態(tài)變量賦值的方法

    這篇文章主要介紹了Spring Boot使用Value注解給靜態(tài)變量賦值的方法,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2018-07-07

最新評(píng)論

澄江县| 康平县| 根河市| 浑源县| 上思县| 济南市| 容城县| 长沙市| 克拉玛依市| 额敏县| 临清市| 栖霞市| 顺义区| 九龙城区| 崇明县| 龙海市| 陕西省| 湟中县| 顺平县| 垦利县| 顺平县| 华容县| 土默特右旗| 湾仔区| 临沂市| 乌恰县| 乐昌市| 长白| 孟津县| 北京市| 福建省| 葵青区| 岳普湖县| 重庆市| 修水县| 和硕县| 澄城县| 阜康市| 卢龙县| 阿尔山市| 宁都县|