Java8函數(shù)式編程真有這么神奇?- [完結(jié)篇]不得不學(xué)的Collector與map...
本文是java8函數(shù)式編程精講stream最后一節(jié),一般最后一節(jié)都是拔高的重頭戲。
我會(huì)重點(diǎn)講解一下Stream中收集器Collector的主要使用方式。這也是實(shí)際編程中經(jīng)常使用到的編程技巧和語法糖。無論是從實(shí)用性還是從知識(shí)擴(kuò)展角度,掌握它的性價(jià)比都非常高。
廢話不多說,直接進(jìn)入正題。
收集器
收集器(Collector)的作用為:將流中元素累積成一個(gè)結(jié)果作用于終端操作collect()上
關(guān)于收集器,我們主要關(guān)注:
????collect()(操作實(shí)現(xiàn)Collector接口的集合)、
????Collector(接口)
????Collectors(工具類)
日常開發(fā)中,我們使用最多的是預(yù)定義收集器功能(Collectors)
它的作用主要是:
????將流元素規(guī)約和匯總為一個(gè)值
????將流元素分組
????將流元素分區(qū)
Collect()方法解析
?????R?collect(Supplier?supplier,??????????????????????初始化結(jié)果容器
????????????????BiConsumer?accumulator,??????添加元素到結(jié)果容器的邏輯
????????????????BiConsumer?combiner);????????????????并行執(zhí)行時(shí)多個(gè)結(jié)果容器的合并方式
我們此處介紹幾個(gè)常用的預(yù)定義收集器方法
集合收集器
????@Test
????public?void?toList()?{
????????List?list?=?CartService.getCartSkuList();
????????List?result?=?list.stream()
????????????????.filter(sku?->?sku.getTotalPrice()?>?100)
????????????????.collect(Collectors.toList());
????????System.out.println(JSON.toJSONString(result,?true));
????}
這個(gè)用例的目的是:選出總價(jià)大于100的商品并打印,也就是大于100的sku會(huì)被保留。
運(yùn)行結(jié)果:
????[
????????{
????????????"skuCategory":"ELECTRONICS",
????????????"skuId":2,
????????????"skuName":"無人機(jī)",
????????????"skuPrice":1000.0,
????????????"totalNum":10,
????????????"totalPrice":1000.0
????????},
????????{
????????????"skuCategory":"ELECTRONICS",
????????????"skuId":1,
????????????"skuName":"VR一體機(jī)",
????????????"skuPrice":2100.0,
????????????"totalNum":10,
????????????"totalPrice":2100.0
????????},
????????{
????????????"skuCategory":"CLOTHING",
????????????"skuId":13,
????????????"skuName":"襯衫",
????????????"skuPrice":120.0,
????????????"totalNum":10,
????????????"totalPrice":120.0
????????}
????]
????Process?finished?with?exit?code?0
集合分組
????@Test
????public?void?group()?{
????????List?list?=?CartService.getCartSkuList();
????????//?key=分組條件??value=元素集合??即Map<分組條件,結(jié)果集合>
????????Map>?result?=?list.stream()
????????????????.collect(Collectors.groupingBy(sku?->?sku.getSkuCategory()));
????????System.out.println(JSON.toJSONString(result,?true));
????}
?這個(gè)用例的目的是:根據(jù)sku類別對(duì)list進(jìn)行分組,分組結(jié)束后返回一個(gè)Map<分組條件,結(jié)果集合>,即key=分組條件 ?value=元素集合
?
運(yùn)行結(jié)果:
????{"CLOTHING":[
????????????{
????????????????"skuCategory":"CLOTHING",
????????????????"skuId":4,
????????????????"skuName":"牛仔褲",
????????????????"skuPrice":60.0,
????????????????"totalNum":10,
????????????????"totalPrice":60.0
????????????},
????????????{
????????????????"skuCategory":"CLOTHING",
????????????????"skuId":13,
????????????????"skuName":"襯衫",
????????????????"skuPrice":120.0,
????????????????"totalNum":10,
????????????????"totalPrice":120.0
????????????}
????????],"BOOKS":[
????????????{
????????????????"skuCategory":"BOOKS",
????????????????"skuId":121,
????????????????"skuName":"Java編程思想",
????????????????"skuPrice":100.0,
????????????????"totalNum":10,
????????????????"totalPrice":100.0
????????????},
????????????{
????????????????"skuCategory":"BOOKS",
????????????????"skuId":3,
????????????????"skuName":"程序化廣告",
????????????????"skuPrice":80.0,
????????????????"totalNum":10,
????????????????"totalPrice":80.0
????????????}
????????],"ELECTRONICS":[
????????????{
????????????????"skuCategory":"ELECTRONICS",
????????????????"skuId":2,
????????????????"skuName":"無人機(jī)",
????????????????"skuPrice":1000.0,
????????????????"totalNum":10,
????????????????"totalPrice":1000.0
????????????},
????????????{
????????????????"skuCategory":"ELECTRONICS",
????????????????"skuId":1,
????????????????"skuName":"VR一體機(jī)",
????????????????"skuPrice":2100.0,
????????????????"totalNum":10,
????????????????"totalPrice":2100.0
????????????}
????????]
????}
從運(yùn)行結(jié)果我們可以看出滿足要求。
集合分區(qū)
集合分區(qū)是分組的一種特例:
?分區(qū)是由一個(gè)謂詞作為分區(qū)函數(shù),分區(qū)函數(shù)返回一個(gè)boolean值最終將分區(qū)結(jié)果分為兩組,一組為boolean=true的 ? 一組為boolean=false的通俗的說也就是滿足條件的分為一組,不滿足條件的為一組
?
????@Test
????public?void?partition()?{
????????List?list?=?CartService.getCartSkuList();
????????Map>?partition?=?list.stream()
????????????????.collect(Collectors.partitioningBy(sku?->?sku.getTotalPrice()?>?100));
????????System.out.println(JSON.toJSONString(partition,?true));
????}
運(yùn)行結(jié)果:
????{
????????false:[
????????????{
????????????????"skuCategory":"CLOTHING",
????????????????"skuId":4,
????????????????"skuName":"牛仔褲",
????????????????"skuPrice":60.0,
????????????????"totalNum":10,
????????????????"totalPrice":60.0
????????????},
????????????{
????????????????"skuCategory":"BOOKS",
????????????????"skuId":121,
????????????????"skuName":"Java編程思想",
????????????????"skuPrice":100.0,
????????????????"totalNum":10,
????????????????"totalPrice":100.0
????????????},
????????????{
????????????????"skuCategory":"BOOKS",
????????????????"skuId":3,
????????????????"skuName":"程序化廣告",
????????????????"skuPrice":80.0,
????????????????"totalNum":10,
????????????????"totalPrice":80.0
????????????}
????????],
????true:[
????????????{
????????????????"skuCategory":"ELECTRONICS",
????????????????"skuId":2,
????????????????"skuName":"無人機(jī)",
????????????????"skuPrice":1000.0,
????????????????"totalNum":10,
????????????????"totalPrice":1000.0
????????????},
????????????{
????????????????"skuCategory":"ELECTRONICS",
????????????????"skuId":1,
????????????????"skuName":"VR一體機(jī)",
????????????????"skuPrice":2100.0,
????????????????"totalNum":10,
????????????????"totalPrice":2100.0
????????????},
????????????{
????????????????"skuCategory":"CLOTHING",
????????????????"skuId":13,
????????????????"skuName":"襯衫",
????????????????"skuPrice":120.0,
????????????????"totalNum":10,
????????????????"totalPrice":120.0
????????????}
????????]
????}
????Process?finished?with?exit?code?0
通過日志打印我們能夠看出,根據(jù)是否滿足斷言將集合分為兩個(gè)組。結(jié)果與分組很像,只不過key變成了true/false。
流高級(jí)操作
接下來是Java8函數(shù)式編程系列的最后一個(gè)章節(jié),到此我們的Stream相關(guān)的講解就暫時(shí)告一段落。
?本節(jié)中我將帶領(lǐng)讀者朋友一起學(xué)習(xí)一下Stream高級(jí)編程相關(guān)的知識(shí)。
?
規(guī)約與匯總
Stream操作中有兩個(gè)相對(duì)高階的概念,分別為規(guī)約和匯總。
規(guī)約(reduce)
????將Stream流中元素轉(zhuǎn)換成一個(gè)值
匯總(collect)
????將Stream流中的元素轉(zhuǎn)換成一個(gè)容器,如Map?、List?、?Set
上文我們已經(jīng)講過了匯總操作Collect,此處我們重點(diǎn)講講規(guī)約操作(reduce)
「對(duì)reduce的理解」
?reduce 操作可以實(shí)現(xiàn)從Stream中生成一個(gè)值,其生成的值不是隨意的,是根據(jù)指定的計(jì)算模型。
?
?比如,之前提到count、min和max方法,因?yàn)槌S枚患{入標(biāo)準(zhǔn)庫中。事實(shí)上,這些方法都是reduce操作。
?
我們看一個(gè)案例
????????/**
????????* reduce 案例1:
????????*??????計(jì)算一批商品的總價(jià)格
????????*/
????????@Test
????????public?void?reduceTest()?{
????????????/**
????????????*?準(zhǔn)備一批訂單數(shù)據(jù)
????????????*/
????????????List?list?=?Lists.newArrayList();
????????????list.add(new?Order(1,?2,?15.12));
????????????list.add(new?Order(2,?5,?257.23));
????????????list.add(new?Order(3,?3,?23331.12));
????????????/**
????????????*?傳統(tǒng)方式
????????????*?1.?計(jì)算商品數(shù)量
????????????*?2.?計(jì)算消費(fèi)總金額
????????????*
????????????*?以下展示Stream的reduce方式
????????????*?思想:分治法
????????????*
????????????*??????U?reduce(U?identity,?????????????????????????????????初始基點(diǎn),此處就是訂單中屬性都是0
????????????*??????????????????BiFunction?accumulator,????計(jì)算邏輯,定義兩個(gè)元素如何進(jìn)行操作
????????????*??????????????????BinaryOperator?combiner);????????????????并行執(zhí)行時(shí)多個(gè)部分結(jié)果的合并方式
????????????*
????????????*/
????????????/**
????????????*?匯總商品數(shù)量和總金額
????????????*/
????????????Order?order?=?list.stream()
????????????????????//.parallel()?????//?并行方式
????????????????????.reduce(
????????????????????????????//?參數(shù)1:初始化值
????????????????????????????new?Order(0,?0,?0.0),
????????????????????????????//?參數(shù)2:Stream中兩個(gè)元素的計(jì)算邏輯
????????????????????????????(Order?order1,?Order?order2)?->?{
????????????????????????????????System.out.println("執(zhí)行?計(jì)算邏輯?方法?。?!");
????????????????????????????????//?計(jì)算兩個(gè)訂單商品數(shù)量和,消費(fèi)金額之和
????????????????????????????????int?productCount?=?order1.getProductCount()?+?order2.getProductCount();
????????????????????????????????double?totalAmount?=?order1.getTotalAmount()?+?order2.getTotalAmount();
????????????????????????????????//?返回計(jì)算結(jié)果
????????????????????????????????return?new?Order(0,?productCount,?totalAmount);
????????????????????????????},
????????????????????????????//?參數(shù)3:并行情況下,多個(gè)并行結(jié)果如何合并
????????????????????????????(Order?order1,?Order?order2)?->?{
????????????????????????????????System.out.println("執(zhí)行?合并?方法?。?!");
????????????????????????????????//?計(jì)算兩個(gè)訂單商品數(shù)量和,消費(fèi)金額之和
????????????????????????????????int?productCount?=?order1.getProductCount()?+?order2.getProductCount();
????????????????????????????????double?totalAmount?=?order1.getTotalAmount()?+?order2.getTotalAmount();
????????????????????????????????//?返回計(jì)算結(jié)果
????????????????????????????????return?new?Order(0,?productCount,?totalAmount);
????????????????????????????});
????????????System.out.println(JSON.toJSONString(order,?true));
????????}
????}
運(yùn)行結(jié)果:
????執(zhí)行?計(jì)算邏輯?方法?。?!
????執(zhí)行?計(jì)算邏輯?方法?。。?br />????執(zhí)行?計(jì)算邏輯?方法?。?!
????{
????????"id":0,
????????"productCount":10,
????????"totalAmount":23603.469999999998
????}
可見通過reduce邏輯,我們能夠很輕松實(shí)現(xiàn)注入復(fù)雜條件的累加,求最值等操作。
reduce規(guī)約操作,實(shí)際上采用了分治思想,提升了編碼和執(zhí)行效率。
更多關(guān)于reduce的解析可以參考 ? https://blog.csdn.net/weixin_41835612/article/details/83687078
4.9 Stream特點(diǎn):
通過上述的介紹,我們能夠總結(jié)出Stream的特點(diǎn):
「無存儲(chǔ)」。stream不是一種數(shù)據(jù)結(jié)構(gòu),它只是某種數(shù)據(jù)源的一個(gè)視圖,數(shù)據(jù)源可以是一個(gè)數(shù)組,Java容器或I/O channel等。
「為函數(shù)式編程而生」。對(duì)stream的任何修改都不會(huì)修改背后的數(shù)據(jù)源,比如對(duì)stream執(zhí)行過濾操作并不會(huì)刪除被過濾的元素,而是會(huì)產(chǎn)生一個(gè)不包含被過濾元素的新stream。
「惰式執(zhí)行」。stream上的操作并不會(huì)立即執(zhí)行,只有等到用戶真正需要結(jié)果的時(shí)候才會(huì)執(zhí)行。
「可消費(fèi)性」。stream只能被“消費(fèi)”一次,一旦遍歷過就會(huì)失效,就像容器的迭代器那樣,想要再次遍歷必須重新生成。
更多Java8特性
由于篇幅及筆者個(gè)人能力有限,不能將Java8的所有特性都詳細(xì)的呈現(xiàn),感興趣的同學(xué)可以自行學(xué)習(xí)。
????Optional
????接口默認(rèn)方法
????新的日期和時(shí)間API
????CompletableFuture:組合式異步編程
????G1垃圾回收器
推薦閱讀
????《Java8實(shí)戰(zhàn)》Java8?In?Action中文版
????《深入理解JVM&G1?GC》
總結(jié)
到此,針對(duì)Java8的新特性Lambda表達(dá)式及stream流式編程的講解就告一段落,希望本系列對(duì)讀者朋友們有所幫助。
這不是結(jié)束,這也不是開始,這是結(jié)束的開始。
