三途凫、流和并行

引入流

  • 流是什么
    集合是Java中使用最多的API,但集合操作卻遠(yuǎn)遠(yuǎn)算不上完美:
    1.很多業(yè)務(wù)邏輯都涉及類似于數(shù)據(jù)庫的操作,比如對幾道菜按照類別進(jìn)行分組 (比如全素 菜肴),或查找出最貴的菜苛谷。你自己用迭代器重新實(shí)現(xiàn)過這些操作多少遍?
    2.要是要處理大量元素又該怎么辦呢?為了提高性能,你需要并行處理,并利用多核架構(gòu)
    流是Java API的新成員,它允許你以聲明性方式處理數(shù)據(jù)集合(通過查詢語句來表達(dá),而不 是臨時(shí)編寫一個(gè)實(shí)現(xiàn))
    1.聲明性——更簡潔,更易讀
    2.可復(fù)合——更靈活
    3.可并行——性能更好

那么,流到底是什么呢?簡短的定義就是“從支持?jǐn)?shù)據(jù)處理操作的源生成的元素序列”

1.元素序列:就像集合一樣,流也提供了一個(gè)接口,可以訪問特定元素類型的一組有序 值。
2.源:流會(huì)使用一個(gè)提供數(shù)據(jù)的源,如集合、數(shù)組或輸入/輸出資源凌唬。
3.數(shù)據(jù)處理操作:流的數(shù)據(jù)處理功能支持類似于數(shù)據(jù)庫的操作,以及函數(shù)式編程語言中 的常用操作,如filter、map漏麦、reduce客税、find、match撕贞、sort等更耻。流操作可以順序執(zhí) 行,也可并行執(zhí)行。
4.流水線:很多流操作本身會(huì)返回一個(gè)流,這樣多個(gè)操作就可以鏈接起來,形成一個(gè)大的流水線捏膨。
5.內(nèi)部迭代:與使用迭代器顯式迭代的集合不同,流的迭代操作是在背后進(jìn)行的秧均。

return dishes.stream()
                .filter(d -> d.getCalories() < 400)
                .sorted(comparing(Dish::getCalories))
                .map(Dish::getName)
                .limit(3)
                .collect(toList());

在本例中,我們先是對menu調(diào)用stream方法,由菜單得到一個(gè)流。數(shù)據(jù)源是菜肴列表(菜 單),它給流提供一個(gè)元素序列号涯。接下來,對流應(yīng)用一系列數(shù)據(jù)處理操作:filter目胡、map、limit 和collect链快。除了collect之外,所有這些操作都會(huì)返回另一個(gè)流,這樣它們就可以接成一條流 水線,于是就可以看作對源的一個(gè)查詢誉己。最后,collect操作開始處理流水線,并返回結(jié)果.
2-流-示例.png
  • 流與集合
    粗略地說,集合與流之間的差異就在于什么時(shí)候進(jìn)行計(jì)算。集合是一個(gè)內(nèi)存中的數(shù)據(jù)結(jié)構(gòu), 它包含數(shù)據(jù)結(jié)構(gòu)中目前所有的值——集合中的每個(gè)元素都得先算出來才能添加到集合中久又。(你可 以往集合里加?xùn)|西或者刪東西,但是不管什么時(shí)候,集合中的每個(gè)元素都是放在內(nèi)存里的,元素 都得先算出來才能成為集合的一部分巫延。)
    相比之下,流則是在概念上固定的數(shù)據(jù)結(jié)構(gòu)(你不能添加或刪除元素),其元素則是按需計(jì) 算的效五。這是一種生產(chǎn)者-消費(fèi)者的關(guān)系。從另一個(gè)角度來說,流就 像是一個(gè)延遲創(chuàng)建的集合:只有在消費(fèi)者要求的時(shí)候才會(huì)計(jì)算值.
    和迭代器類似,流只能遍歷一次炉峰。遍歷完之后,我們就說這個(gè)流已經(jīng)被消費(fèi)掉了.

外部迭代和內(nèi)部迭代

使用Collection接口需要用戶去做迭代(比如用for-each),這稱為外部迭代畏妖。 相反, Streams庫使用內(nèi)部迭代——它幫你把迭代做了,還把得到的流值存在了某個(gè)地方,你只要給出 一個(gè)函數(shù)說要干什么就可以了.

  • 流操作


    2-流-操作.png

1.中間操作
諸如filter或sorted等中間操作會(huì)返回另一個(gè)流。這讓多個(gè)操作可以連接起來形成一個(gè)查 詢疼阔。重要的是,除非流水線上觸發(fā)一個(gè)終端操作,否則中間操作不會(huì)執(zhí)行任何處理
2.終端操作
終端操作會(huì)從流的流水線生成結(jié)果
3.流使用
總而言之,流的使用一般包括三件事:

  • 一個(gè)數(shù)據(jù)源(如集合)來執(zhí)行一個(gè)查詢;
  • 一個(gè)中間操作鏈,形成一條流的流水線;
  • 一個(gè)終端操作,執(zhí)行流水線,并能生成結(jié)果

使用流

讓Stream API管理如何處理數(shù)據(jù)戒劫。這樣Stream API就可 以在背后進(jìn)行多種優(yōu)化。使用內(nèi)部迭代的話,Stream API可以決定并行運(yùn)行你的代碼婆廊。這 要是用外部迭代的話就辦不到了,因?yàn)槟阒荒苡脝我痪€程挨個(gè)迭代迅细。

  • 篩選切片


    2-流-篩選.png

    篩選各異的元素
    截?cái)嗔?br> 跳過

  • 映射
    map

flatMap


2-流-flatmap.png
  • 查找匹配

  • 歸約
    如何把一個(gè)流中的元素組合起來,使用reduce操作來表達(dá)更復(fù)雜的查
    詢,比如“計(jì)算菜單中的總卡路里”或“菜單中卡路里最高的菜是哪一個(gè)”。此類查詢需要將流中所有元素反復(fù)結(jié)合起來,得到一個(gè)值,比如一個(gè)Integer淘邻。


    2-流-reduce.png
  • 交易員Demo
    (1) 找出2011年發(fā)生的所有交易,并按交易額排序(從低到高)茵典。
    (2) 交易員都在哪些不同的城市工作過?
    (3) 查找所有來自于劍橋的交易員,并按姓名排序。
    (4) 返回所有交易員的姓名字符串,按字母順序排序宾舅。
    (5) 有沒有交易員是在米蘭工作的?
    (6) 打印生活在劍橋的交易員的所有交易額统阿。 (7) 所有交易中,最高的交易額是多少?
    (8) 找到交易額最小的交易。

  • 數(shù)值流

  • 構(gòu)建流
    1.值創(chuàng)建類
    2.數(shù)組生成流
    3.文件生成流
    4.由函數(shù)生成流筹我,創(chuàng)建無限流

用流收集數(shù)據(jù)

  • 歸約和匯總
    collect是一個(gè)歸約操作,就像reduce一樣可以接 受各種做法作為參數(shù),將流中的元素累積成一個(gè)匯總結(jié)果扶平。具體的做法是通過定義新的 Collector接口來定義的,因此區(qū)分Collection、Collector和collect是很重要的
    下面是一些查詢的例子,看看你用collect和收集器能夠做什么蔬蕊。
    1.對一個(gè)交易列表按貨幣分組,獲得該貨幣的所有交易額總和(返回一個(gè)Map<Currency,Integer>)结澄。
    2.將交易列表分成兩組:貴的和不貴的(返回一個(gè)Map<Boolean, List<Transaction>>)。
    3.創(chuàng)建多級分組,比如按城市對交易分組,然后進(jìn)一步按照貴或不貴分組(返回一個(gè) Map<Boolean, List<Transaction>>)

前一個(gè)例子清楚地展示了函數(shù)式編程相對于指令式編程的一個(gè)主要優(yōu)勢:你只需指出希望的 結(jié)果——“做什么”,而不用操心執(zhí)行的步驟——“如何做”岸夯。在上一個(gè)例子里,傳遞給collect 方法的參數(shù)是Collector接口的一個(gè)實(shí)現(xiàn),也就是給Stream中元素做匯總的方法麻献,toList只是說“按順序給每個(gè)元素生成一個(gè)列表”;在本例中,groupingBy說的是“生成一個(gè) Map,它的鍵是(貨幣)桶,值則是桶中那些元素的列表”。


2-流-收集.png
  • 預(yù)定義收集器
    預(yù)定義收集器的功能,也就是那些可以從Collectors 類提供的工廠方法(例如groupingBy)創(chuàng)建的收集器猜扮。它們主要提供了三大功能:
    1.將流元素歸約和匯總為一個(gè)值
    2.元素分組
    3.元素分區(qū)

  • 歸約 匯總

  • 廣義的歸約匯總
    reducing方法創(chuàng)建的收集器
    起始值
    轉(zhuǎn)換函數(shù)
    累計(jì)函數(shù)

  • 分組


    2-流-分組.png
2-流-分組多級.png
2-流-分組嵌套.png
  • 分區(qū)
    分區(qū)是分組的特殊情況:由一個(gè)謂詞(返回一個(gè)布爾值的函數(shù))作為分類函數(shù),它稱分區(qū)函 數(shù)赎瑰。分區(qū)函數(shù)返回一個(gè)布爾值,這意味著得到的分組Map的鍵類型是Boolean,于是它最多可以 分為兩組——true是一組,false是一組

  • 收集器接口
    自定義歸約操作


    順序歸約過程.png

并行數(shù)據(jù)處理與性能

  • 并行流
    在Java 7之前,并行處理數(shù)據(jù)集合非常麻煩破镰。第一,你得明確地把包含數(shù)據(jù)的數(shù)據(jù)結(jié)構(gòu)分成若干子部分压储。第二鲜漩,你要給每個(gè)子部分分配一個(gè)獨(dú)立的線程。第三集惋,你需要在恰當(dāng)?shù)臅r(shí)候?qū)λ鼈冞M(jìn)行同步來避免不希望出現(xiàn)的競爭條件孕似,等待所有線程完成,最后把這些部分結(jié)果合并起來刮刑。 Java 7引入了一個(gè)叫作分支/合并的框架喉祭,讓這些操作更穩(wěn)定养渴、更不易出錯(cuò)

高效使用并行流

把順序流轉(zhuǎn)成并行流輕而易舉,但卻不一定是好事泛烙。我們在本節(jié)中
已經(jīng)指出理卑,并行流并不總是比順序流快。此外蔽氨,并行流有時(shí)候會(huì)和你的直覺不一致藐唠,所
以在考慮選擇順序流還是并行流時(shí),第一個(gè)也是最重要的建議就是用適當(dāng)?shù)幕鶞?zhǔn)來檢查
其性能
1.留意裝箱鹉究。自動(dòng)裝箱和拆箱操作會(huì)大大降低性能宇立。 Java 8中有原始類型流(IntStream、
LongStream自赔、 DoubleStream)來避免這種操作妈嘹,但凡有可能都應(yīng)該用這些流
2.有些操作本身在并行流上的性能就比順序流差。特別是limit和findFirst等依賴于元
素順序的操作绍妨,它們在并行流上執(zhí)行的代價(jià)非常大
3.還要考慮流的操作流水線的總計(jì)算成本
4.對于較小的數(shù)據(jù)量润脸,選擇并行流幾乎從來都不是一個(gè)好的決定
5.考慮流背后的數(shù)據(jù)結(jié)構(gòu)是否易于分解
6.考慮終端操作中合并步驟的代價(jià)是大是小

  • fork/join


    分支合并過程.png
  • Spliterator

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市痘绎,隨后出現(xiàn)的幾起案子津函,更是在濱河造成了極大的恐慌,老刑警劉巖孤页,帶你破解...
    沈念sama閱讀 206,126評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件尔苦,死亡現(xiàn)場離奇詭異,居然都是意外死亡行施,警方通過查閱死者的電腦和手機(jī)允坚,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來蛾号,“玉大人稠项,你說我怎么就攤上這事∠式幔” “怎么了展运?”我有些...
    開封第一講書人閱讀 152,445評論 0 341
  • 文/不壞的土叔 我叫張陵,是天一觀的道長精刷。 經(jīng)常有香客問我拗胜,道長,這世上最難降的妖魔是什么怒允? 我笑而不...
    開封第一講書人閱讀 55,185評論 1 278
  • 正文 為了忘掉前任埂软,我火速辦了婚禮,結(jié)果婚禮上纫事,老公的妹妹穿的比我還像新娘勘畔。我一直安慰自己所灸,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,178評論 5 371
  • 文/花漫 我一把揭開白布炫七。 她就那樣靜靜地躺著爬立,像睡著了一般。 火紅的嫁衣襯著肌膚如雪诉字。 梳的紋絲不亂的頭發(fā)上懦尝,一...
    開封第一講書人閱讀 48,970評論 1 284
  • 那天,我揣著相機(jī)與錄音壤圃,去河邊找鬼陵霉。 笑死,一個(gè)胖子當(dāng)著我的面吹牛伍绳,可吹牛的內(nèi)容都是我干的踊挠。 我是一名探鬼主播,決...
    沈念sama閱讀 38,276評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼冲杀,長吁一口氣:“原來是場噩夢啊……” “哼效床!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起权谁,我...
    開封第一講書人閱讀 36,927評論 0 259
  • 序言:老撾萬榮一對情侶失蹤剩檀,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后旺芽,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體沪猴,經(jīng)...
    沈念sama閱讀 43,400評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,883評論 2 323
  • 正文 我和宋清朗相戀三年采章,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了运嗜。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 37,997評論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡悯舟,死狀恐怖担租,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情抵怎,我是刑警寧澤奋救,帶...
    沈念sama閱讀 33,646評論 4 322
  • 正文 年R本政府宣布,位于F島的核電站反惕,受9級特大地震影響菠镇,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜承璃,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,213評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望蚌本。 院中可真熱鬧盔粹,春花似錦隘梨、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,204評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至进萄,卻和暖如春捻脖,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背中鼠。 一陣腳步聲響...
    開封第一講書人閱讀 31,423評論 1 260
  • 我被黑心中介騙來泰國打工可婶, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人援雇。 一個(gè)月前我還...
    沈念sama閱讀 45,423評論 2 352
  • 正文 我出身青樓矛渴,卻偏偏與公主長得像,于是被迫代替她去往敵國和親惫搏。 傳聞我的和親對象是個(gè)殘疾皇子具温,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,722評論 2 345

推薦閱讀更多精彩內(nèi)容