<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Java8函數(shù)式編程真有這么神奇?- 案例展示lambda表達(dá)式對編碼效率提升到底有多大

          共 7605字,需瀏覽 16分鐘

           ·

          2022-06-08 07:31


          這是一個(gè)新的系列,主要講Java8的lambda編程以及Stream流式編程相關(guān)的用法和案例。

          這個(gè)系列脫胎于一個(gè)內(nèi)部的分享,由于篇幅較長,內(nèi)容較多,因此拆分成多篇文章進(jìn)行發(fā)布,方便自己后續(xù)參考,也希望能夠幫到讀者朋友。

          本文是Java8函數(shù)式編程系列的第一篇,我們一起學(xué)習(xí)一下Java8函數(shù)式編程的基本概念及操作。

          1.概述Lambda表達(dá)式

          ?

          Lambda 表達(dá)式,也可稱為閉包,它是推動(dòng) Java 8 發(fā)布的最重要新特性。

          Lambda 允許把函數(shù)作為一個(gè)方法的參數(shù)(函數(shù)作為參數(shù)傳遞進(jìn)方法中)。

          ?

          使用 Lambda 表達(dá)式可以使代碼變的更加簡潔緊湊。

          1.1 lambda表達(dá)式語法

          ????(parameters)?->?expression
          ????或
          ????(parameters)?->?{statement};
          1. 我們可以將lambda表達(dá)式理解為一種代替原先匿名函數(shù)的新的編程方式

          2. 通過使用lambda表達(dá)式替換匿名函數(shù)的形式,將lambda表達(dá)式作為方法參數(shù),實(shí)現(xiàn)判斷邏輯參數(shù)化傳遞的目的。

          1.2 lambda表達(dá)式形式

          無參數(shù)

          ????()?->?System.out.println("code");

          只有一個(gè)參數(shù)

          ????name?->?System.out.println("hello:"?+?name?+?"!");

          沒有參數(shù),且邏輯復(fù)雜,需要通過大括號將多個(gè)語句括起來

          ????()?->?{????
          ????????System.out.println("hello");????
          ????????System.out.println("lambda");
          ????}

          包含兩個(gè)參數(shù)的方法

          ????BinaryOperator?functionAdd?=?(x,?y)?->?x?+?y;
          ????Long?result?=?functionAdd.apply(1L,?2L);

          包含兩個(gè)參數(shù)且對參數(shù)顯式聲明

          ????BinaryOperator?functionAdd?=?(Long?x,?Long?y)?->?x?+?y;
          ????Long?result?=?functionAdd.apply(1L,?2L);

          1.3 函數(shù)式接口

          定義:

          ?

          一個(gè)接口有且只有一個(gè)抽象方法;

          ?

          函數(shù)式接口的實(shí)例可以通過 lambda 表達(dá)式、方法引用或者構(gòu)造方法引用來創(chuàng)建;

          「注意」

          如果一個(gè)接口只有一個(gè)抽象方法,那么該接口就是一個(gè)函數(shù)式接口

          如果我們在某個(gè)接口上聲明了 「@FunctionalInterface」 注解,那么編譯器就會(huì)按照函數(shù)式接口的定義來要求該接口

          @FunctionInterface是Java8函數(shù)式接口注解,屬于聲明式注解,幫助編譯器校驗(yàn)被標(biāo)注的接口是否符合函數(shù)式接口定義

          1.4 方法引用

          我們通過Lambda表達(dá)式來實(shí)現(xiàn)匿名方法。

          有些情況下,使用Lambda表達(dá)式僅僅是調(diào)用一些已經(jīng)存在的方法;除了調(diào)用動(dòng)作外,沒有其他任何多余的動(dòng)作,在這種情況下我們傾向于通過方法名來調(diào)用它,而Lambda表達(dá)式可以幫助我們實(shí)現(xiàn)這一要求。它使得Lambda在調(diào)用那些已經(jīng)擁有方法名的方法的代碼更簡潔、更容易理解。

          方法引用可以理解為Lambda表達(dá)式的另外一種表現(xiàn)形式。

          ?

          方法引用是調(diào)用特定方法的lambda表達(dá)式的一種快捷寫法,可以讓你重復(fù)使用現(xiàn)有的方法定義,并像lambda表達(dá)式一樣傳遞他們。

          ?

          注意:

          使用方法引用時(shí),只寫方法名,不寫括號

          1.4.1 方法引用格式:

          ????格式:?目標(biāo)引用????雙冒號分隔符???方法名

          ????eg:??String???????::????????valueOf

          1.4.2 方法引用分類:

          1.指向靜態(tài)方法的方法引用:當(dāng)Lambda表達(dá)式的結(jié)構(gòu)體是一個(gè)對象,并且調(diào)用其靜態(tài)方法時(shí),使用如下方式

          ????表達(dá)式:
          ????????(args)?->?ClassName::staticMethod(args);
          ????格式:????ClassName?::?staticMethodName
          ????eg:?????Integer???::?valueOf

          2.指向任意類型實(shí)例方法的方法引用:當(dāng)直接調(diào)用對象的實(shí)例方法,則使用如下方式進(jìn)行調(diào)用

          ????表達(dá)式:
          ????????????(args)?->?args.instanceMethod();
          ????格式:??ClassName::instanceMethod;
          ????eg:???String::indexOf??
          ????????????????String::toString

          3.指向現(xiàn)有對象的實(shí)例方法的方法引用:通過對象實(shí)例,方法引用實(shí)例方法

          ????表達(dá)式:
          ????????(args)?->?object.instanceMethod(args);
          ????????改寫為
          ????????(args)?->??object::instanceMethod;
          ????eg:
          ????????StringBuilder?sb?=?new?StringBuilder();
          ????????Consumer?consumer?=?(String?str)?->?stringBuilder.append(str);

          就可以改寫為

          ????Consumer?consumer?=?(String?str)?->?stringBuilder::append;

          2.從一個(gè)案例入手

          我們先看一個(gè)例子,宏觀感受一下Java8 Lambda編程帶來的便利(后續(xù)講解Stream同樣使用該案例)

          2.1 案例:直觀體驗(yàn)Java8Stream操作

          Sku實(shí)體類: ?標(biāo)識一個(gè)電商下單商品信息對象

          ????public?class?Sku?{
          ????????//?商品編號
          ????????private?Integer?skuId;
          ????????//?商品名稱
          ????????private?String?skuName;
          ????????//?單價(jià)
          ????????private?Double?skuPrice;
          ????????//?購買個(gè)數(shù)
          ????????private?Integer?totalNum;
          ????????//?總價(jià)
          ????????private?Double?totalPrice;
          ????????//?商品類型
          ????????private?Enum?skuCategory;

          ????????public?Sku()?{
          ????????}

          ????????public?Sku(Integer?skuId,?String?skuName,?Double?skuPrice,?Integer?totalNum,?Double?totalPrice,?Enum?skuCategory)?{
          ????????????this.skuId?=?skuId;
          ????????????this.skuName?=?skuName;
          ????????????this.skuPrice?=?skuPrice;
          ????????????this.totalNum?=?totalNum;
          ????????????this.totalPrice?=?totalPrice;
          ????????????this.skuCategory?=?skuCategory;
          ????????}
          ????????????...省略getter??setter...
          ????}

          SkuCategoryEnum枚舉類: 商品類型枚舉

          ????public?enum?SkuCategoryEnum?{
          ????????CLOTHING(10,?"服務(wù)類"),
          ????????ELECTRONICS(20,?"數(shù)碼產(chǎn)品類"),
          ????????SPORTS(30,?"運(yùn)動(dòng)類"),
          ????????BOOKS(40,?"圖書類")
          ????????;

          ????????//?商品類型編號
          ????????private?Integer?code;
          ????????//?商品名稱
          ????????private?String?name;

          ????????SkuCategoryEnum(Integer?code,?String?name)?{
          ????????????this.code?=?code;
          ????????????this.name?=?name;
          ????????}
          ????????...省略getter...
          ????}

          CartService類: 初始化一批數(shù)據(jù),模擬購物車

          ????public?class?CartService?{
          ????????//?初始化購物車
          ????????private?static?List?cartSkuList?=?new?ArrayList<>();

          ????????static?{
          ????????????cartSkuList.add(new?Sku(2,?"無人機(jī)",?1000.00,?10,?1000.00,?SkuCategoryEnum.ELECTRONICS));
          ????????????cartSkuList.add(new?Sku(1,?"VR一體機(jī)",?2100.00,?10,?2100.00,?SkuCategoryEnum.ELECTRONICS));
          ????????????cartSkuList.add(new?Sku(4,?"牛仔褲",?60.00,?10,?60.00,?SkuCategoryEnum.CLOTHING));
          ????????????cartSkuList.add(new?Sku(13,?"襯衫",?120.00,?10,?120.00,?SkuCategoryEnum.CLOTHING));
          ????????????cartSkuList.add(new?Sku(121,?"Java編程思想",?100.00,?10,?100.00,?SkuCategoryEnum.BOOKS));
          ????????????cartSkuList.add(new?Sku(3,?"程序化廣告",?80.00,?10,?80.00,?SkuCategoryEnum.BOOKS));
          ????????}

          ????????public?static?List?getCartSkuList()?{
          ????????????return?cartSkuList;
          ????????}
          ????}

          我們直接看這個(gè)案例

          ????private?static?List?cartSkuList?=?CartService.getCartSkuList();

          ????@Test
          ????public?void?show()?{
          ????????List?collect?=?cartSkuList.stream()
          ????????????????//?方法引用
          ????????????????.map(Sku::getSkuId)
          ????????????????.distinct()
          ????????????????.sorted()
          ????????????????.collect(Collectors.toList());
          ????????collect.stream().forEach(skuId?->?{
          ????????????System.out.println(skuId.toString());
          ????????});
          ????}

          簡單解釋下這段代碼的意圖:

          首先獲取購物車中商品列表,將該列表轉(zhuǎn)換為流;收集商品列表中的所有商品編號(skuId),對商品編號進(jìn)行去重,并進(jìn)行自然排序(升序排列),最后收集為一個(gè)商品編號集合,并對該集合進(jìn)行遍歷打印。

          我并沒有加注釋,但是相信聰明的你也一定能讀懂上面這段代碼,這正是Stream編程的特點(diǎn):方法名見名知意,流式編程方式符合人類思考邏輯

          運(yùn)行該用例,打印如下:

          ????1
          ????2
          ????3
          ????4
          ????13
          ????121

          打印結(jié)果符合我們的預(yù)期意圖。

          想象一下,如果不使用lambda+Stream方式,而是使用java7及之前的傳統(tǒng)集合操作,實(shí)現(xiàn)上述操作我們的代碼量有多少?保守估計(jì)至少是上述代碼段的1.5倍。

          這個(gè)案例可能還不具備說服力,接下來的文章中,我將通過一個(gè)對比案例來比較lambda編程與傳統(tǒng)方式對集合操作的效率提升。

          ?

          那么,使用了lambda函數(shù)式編程之后,對我們的開發(fā)真有顯著的提升么?

          ?

          在接下來的章節(jié)中,我們通過一個(gè)實(shí)戰(zhàn)案例對比原始集合操作與Stream集合操作具體有哪些不同,直觀地展示Stream集合操作對編程效率的提升。

          案例:對比原始集合操作與Stream集合操作

          需求場景:

          針對上面的購物車,我們想要

          1. 全局查看購物車中都有哪些商品
          2. 將購物車中的圖書類商品進(jìn)行過濾(刪除圖書類商品)
          3. 在其余商品中挑選兩件最貴的
          4. 打印出上述兩件商品的名稱和總價(jià)

          原始集合操作:

          ????@Test
          ????public?void?traditionalWay()?{
          ????????//?1.?打印所有商品
          ????????List?skus?=?CartService.getCartSkuList();
          ????????for?(Sku?sku?:?skus)?{
          ????????????System.out.println(JSON.toJSONString(sku,?true));
          ????????}
          ????????//?2.?過濾圖書類商品
          ????????List?notIncludeBooksList?=?new?ArrayList<>();
          ????????for?(Sku?sku?:?skus)?{
          ????????????if?(!sku.getSkuCategory().equals(SkuCategoryEnum.BOOKS))?{
          ????????????????notIncludeBooksList.add(sku);
          ????????????}
          ????????}
          ????????//?3.?其余商品中挑選兩件最貴的?價(jià)格倒排序,取top2
          ????????//?3.1?先排序
          ????????notIncludeBooksList.sort(new?Comparator()?{
          ????????????@Override
          ????????????public?int?compare(Sku?sku0,?Sku?sku1)?{
          ????????????????if?(sku0.getTotalPrice()?>?sku1.getTotalPrice())?{
          ????????????????????return?-1;
          ????????????????}
          ????????????????if?(sku0.getTotalPrice()?????????????????????return?1;
          ????????????????}
          ????????????????return?0;
          ????????????}
          ????????});

          ????????//?3.2?取top2
          ????????List?top2SkuList?=?new?ArrayList<>();
          ????????for?(int?i?=?0;?i?????????????top2SkuList.add(notIncludeBooksList.get(i));
          ????????}
          ????????//?4.?打印出上述兩件商品的名稱和總價(jià)
          ????????//?4.1?求兩件商品總價(jià)
          ????????double?totalMoney?=?0.0;
          ????????for?(Sku?sku?:?top2SkuList)?{
          ????????????totalMoney?+=?sku.getTotalPrice();
          ????????}
          ????????//?4.2?獲取兩件商品名稱
          ????????List?resultSkuNameList?=?new?ArrayList<>();
          ????????for?(Sku?sku?:?top2SkuList)?{
          ????????????resultSkuNameList.add(sku.getSkuName());
          ????????}
          ????????//?打印輸出結(jié)果
          ????????System.out.println("結(jié)果商品名稱:?"?+?JSON.toJSONString(resultSkuNameList,?true));
          ????????System.out.println("商品總價(jià):"?+?totalMoney);
          ????}

          運(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":4,"skuName":"牛仔褲","skuPrice":60.0,"totalNum":10,"totalPrice":60.0}
          ????{"skuCategory":"CLOTHING","skuId":13,"skuName":"襯衫","skuPrice":120.0,"totalNum":10,"totalPrice":120.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}
          ????結(jié)果商品名稱:?[
          ????????"VR一體機(jī)",
          ????????"無人機(jī)"
          ????]
          ????商品總價(jià):3100.0

          我們可以看到傳統(tǒng)的集合操作還是寫了比較多的代碼,而且在編碼過程中為了滿足各種要求,我們通過聲明新的容器來接受過程中的操作結(jié)果,這帶來了內(nèi)存使用量的增加。

          接下來看一下Stream方式下如何編碼實(shí)現(xiàn)我們的需求:

          Stream集合操作:

          ????@Test
          ????public?void?streamWay()?{
          ????????AtomicReference?money?=?new?AtomicReference<>(Double.valueOf(0.0));
          ????????List?resultSkuNameList?=?CartService.getCartSkuList()
          ????????????//?獲取集合流
          ????????????????.stream()
          ????????????????/**1.?打印商品信息*/
          ????????????????.peek(sku?->?System.out.println(JSON.toJSONString(sku)))
          ????????????????/**2.?過濾掉所有的圖書類商品*/
          ????????????????.filter(sku?->?!SkuCategoryEnum.BOOKS.equals(sku.getSkuCategory()))
          ????????????????/**3.?價(jià)格進(jìn)行排序,默認(rèn)是從小到大,調(diào)用reversed進(jìn)行翻轉(zhuǎn)排序即從大到小*/
          ????????????????.sorted(Comparator.comparing(Sku::getTotalPrice).reversed())
          ????????????????/**4.?取top2*/
          ????????????????.limit(2)
          ????????????????/**累加金額*/
          ????????????????.peek(sku?->?money.set(money.get()?+?sku.getTotalPrice()))
          ????????????????/**獲取商品名稱*/
          ????????????????.map(sku?->?sku.getSkuName())
          ????????????????.collect(Collectors.toList());
          ????????System.out.println("商品總價(jià):"?+?money.get());
          ????????System.out.println("商品名列表:"?+?JSON.toJSONString(resultSkuNameList));
          ????}

          運(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":4,"skuName":"牛仔褲","skuPrice":60.0,"totalNum":10,"totalPrice":60.0}
          ????{"skuCategory":"CLOTHING","skuId":13,"skuName":"襯衫","skuPrice":120.0,"totalNum":10,"totalPrice":120.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}
          ????商品總價(jià):3100.0
          ????商品名列表:["VR一體機(jī)","無人機(jī)"]

          我們可以看到,通過Stream集合操作,運(yùn)行結(jié)果與傳統(tǒng)集合操作完全一致。但是編碼量卻能夠顯著減少。

          「辯證的分析一下」,如果對Stream操作沒有一個(gè)較為明確的了解,閱讀這段代碼確實(shí)有些難度,但是只要有一點(diǎn)了解,Stream集合操作代碼帶來的無論是編碼量顯著降低還是可讀性提升,亦或是內(nèi)存空間的節(jié)約都是可觀的。

          階段小結(jié)

          可見,學(xué)習(xí)并運(yùn)用Lambda及Stream編程,對于提升我們的編碼效率以及提升代碼可讀性都有著明顯的收益。

          參考資料

          「《告別996,開啟Java高效編程之門》慕課網(wǎng)」



          瀏覽 65
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評論
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  欧美一级A片在线观看 | 国产色情AAA级AAA电影 | 美女被曹91 | 伊人激情视频 | 国产免费AV片在线无码免费看 |