1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        滿滿干貨?。?!lambda表達(dá)式使用總結(jié)

        共 6642字,需瀏覽 14分鐘

         ·

        2021-11-30 07:35

        前言

        最近一段時(shí)間一直在趕項(xiàng)目,所以也比較忙,今天我抽一些時(shí)間,把整個(gè)項(xiàng)目參與的收獲和各位小伙伴分享下,也算是一個(gè)小階段的總結(jié),不過今天的主題是lambda,所以只能算是局部知識點(diǎn)的總結(jié)。

        由于整個(gè)項(xiàng)目都采用redis存儲數(shù)據(jù)(你沒聽錯(cuò),就是拿redis當(dāng)數(shù)據(jù)庫),所以在數(shù)據(jù)讀寫方面,特別是查詢的時(shí)候,很多場景下都是拿到數(shù)據(jù)后,還需要手動處理下,所以本次對lambda使用的頻次特別高,今天我們就先抽點(diǎn)時(shí)間做一個(gè)簡單的梳理和總結(jié)。

        拉姆達(dá)表達(dá)式

        提到lambda表達(dá)式,想必各位小伙伴一定不會感到陌生,lambdaJDK1.8引入的新特性,而且經(jīng)常在面試的時(shí)候被問道,更重要的是使用lambda確實(shí)也可以讓我們的代碼更簡潔,很多場景也更容易實(shí)現(xiàn)。前面我們也分享過lambda的相關(guān)知識點(diǎn),感興趣的小伙伴可以去看下:




        今天我們主要lambda中的以下幾塊內(nèi)容,這幾個(gè)也是本次用的頻次比較高的:

        • map
        • filter
        • joining
        • max
        • 排序
        • flatMap
        • collect

        map

        map其實(shí)就類似于一個(gè)處理器,我們可以通過map方法將現(xiàn)有對象的集合轉(zhuǎn)換成我們需要的數(shù)據(jù)類型。

        過濾屬性

        其中最常見的用法就是從一個(gè)對象實(shí)例的list中,過濾出對象實(shí)例的某個(gè)屬性,比如User的name`屬性:

        User?user1?=?new?User(1L,?"syske");
        User?user2?=?new?User(2L,?"yun?zhong?zhi");
        List?userList?=?Lists.newArrayList(user1,?user2);
        //?用戶
        System.out.printf("userList?=?%s\n",userList);
        //?用戶名list
        List?userNameList?=?userList.stream().map(User::getUsername).collect(Collectors.toList());
        System.out.printf("userNameList?=?%s\n",userNameList);

        運(yùn)行結(jié)果如下:

        user源碼:

        public?class?User?{
        ????/**
        ?????*?用戶?id
        ?????*/

        ????private?Long?id;
        ????/**
        ?????*?用戶名
        ?????*/

        ????private?String?username;
        ????/*
        ????省略getter/setter方法
        ????*/

        }

        這里我們其實(shí)也演示了collect方法的簡單用法,collect方法的作用就是將我們map處理之后的數(shù)據(jù)流收集起來,后面我們還會詳細(xì)演示它的用法。

        轉(zhuǎn)換類型

        這里說的類型轉(zhuǎn)換主要也是將List中的數(shù)據(jù)轉(zhuǎn)換成我們需要的,比如將user轉(zhuǎn)換為String:

        List?userStrList?=?userList.stream().map(String::valueOf).collect(Collectors.toList());

        或者轉(zhuǎn)換為json:

        List?collect?=?userList.stream().map(JSON::toJSONString).collect(Collectors.toList());

        或者將String轉(zhuǎn)成Integer:

        ArrayList?strings?=?Lists.newArrayList("0912",?"1930",?"1977",?"1912");
        List?integers?=?strings.stream().map(Integer::parseInt).collect(Collectors.toList());

        當(dāng)然,map的用法會有很多,感興趣的小伙伴自行探索下吧,下面我們看下其他lambda表達(dá)式的用法。

        filter

        filter也是日常開發(fā)中經(jīng)常用到的一個(gè)表達(dá)式,而且非常好用,比如數(shù)據(jù)檢索、數(shù)據(jù)過濾等:

        ArrayList?countList?=?Lists.newArrayList(89,?97,?99,?12,?15,?45,?55,?35,?25,?18);
        //?過濾大于?40的數(shù)據(jù)
        List?collect1?=?countList.stream().filter(a?->?a?>?40).collect(Collectors.toList());
        //?過濾結(jié)果:[89, 97, 99, 45, 55]

        過濾用戶名中包含s的用戶:

        User?user1?=?new?User(1L,?"syske");
        User?user2?=?new?User(2L,?"yun?zhong?zhi");
        List?userList?=?Lists.newArrayList(user1,?user2);
        //?過濾username包含s的用戶
        List?users?=?userList.stream().filter(user?->?user.getUsername().contains("s")).collect(Collectors.toList());

        joining

        joining主要是針對List聚合成string的場景,它主要用于需要將集合中的元素通過特定的符號拼接,比如,分割:

        ArrayList?ages?=?Lists.newArrayList(89,?97,?99,?12,?15,?45,?55,?35,?25,?18);
        String?collect2?=?ages.stream().map(String::valueOf).collect(Collectors.joining(","));
        //?運(yùn)行結(jié)果:89,97,99,12,15,45,55,35,25,18

        因?yàn)樽罱K的結(jié)果是String,所以在joining之前先要通過map處理下數(shù)據(jù),如果數(shù)據(jù)是String類型,則可以直接操作:

        ArrayList?strings?=?Lists.newArrayList("0912",?"1930",?"1977",?"1912");
        String?collect3?=?strings.stream().collect(Collectors.joining());

        嚴(yán)格來說,joining操作屬于collect方法的范疇,和Collectors.toList屬于同一類操作。

        max

        max就很簡單了,就是獲取集合中的最大值,和min相對。支持對數(shù)字、字符串等數(shù)據(jù)進(jìn)行操作:

        Optional?max?=?ages.stream().max(Comparator.naturalOrder());
        System.out.println(max.get());

        這里的Comparator.naturalOrder就是安裝自然順序排序,也就是9-1,或者z-a,我測試的時(shí)候發(fā)現(xiàn),如果存在相同字符(但是大小寫不同,針對字符串),排序的時(shí)候是按照小寫大于大寫的規(guī)則進(jìn)行排序的,具體可以看下面的截圖:

        如果數(shù)據(jù)是數(shù)字:

        ArrayList?ages?=?Lists.newArrayList(89,?97,?99,?12,?15,?45,?55,?35,?25,?18);
        Optional?max?=?ages.stream().max(Comparator.naturalOrder());
        //?運(yùn)行結(jié)果:99

        這里有個(gè)騷操作,如果把Comparator.naturalOrder()改成Comparator.reverseOrder()獲取到的就是最小值,我在想如果你就是想寫這樣一個(gè)讓別人想不到的bug,你可以試試這樣操作,看會不會被打死。

        排序

        排序其實(shí)在上面max以及有提及了,這里我們再詳細(xì)看下。一般我們并不會單通過stream進(jìn)行排序,因?yàn)槌S玫募匣旧隙继峁┝伺判蚍椒?,比?code style="overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 100, 65);">List:

        ArrayList?strings2?=?Lists.newArrayList("ab",?"ba",?"ca",?"dd",?"Zf",?"ZF",?"zF",?"zf",?"cl");
        strings2.sort(Comparator.naturalOrder());

        只有在stream處理之后,數(shù)據(jù)還要求有順序要求的時(shí)候,我們才會通過stream進(jìn)行排序:

        ArrayList?ages?=?Lists.newArrayList(89,?97,?99,?12,?15,?45,?55,?35,?25,?18);
        List?collect1?=?ages.stream().filter(a?->?a?>?40).sorted(Comparator.naturalOrder()).collect(Collectors.toList());

        這樣處理完成之后的數(shù)據(jù)就是有序集合了,這樣的性能會比拿到集合之后再排序要好。

        flatMap

        flatMapmap有點(diǎn)像,當(dāng)然區(qū)別也挺大,map其實(shí)就類似水管中單進(jìn)單出的處理器,進(jìn)去多少個(gè),出來多少個(gè),而flatMap是單入多出的處理器,進(jìn)去一個(gè),出來可能是多個(gè)。下面我們看下他們的對比:

        //?原始數(shù)據(jù)
        ArrayList?strings3?=?Lists.newArrayList("09,12",?"19,30",?"19,77",?"19,12");
        //?map處理
        List?collect4?=?strings3.stream().map(string?->?string.split(",")).collect(Collectors.toList());
        //?flatMap
        List?collect5?=?strings3.stream().flatMap(string?->?Arrays.stream(string.split(","))).collect(Collectors.toList());

        可以看到map只能把數(shù)據(jù)最終分割成與原有元素?cái)?shù)量相等的數(shù)據(jù)數(shù)組,而flatMap這里可以進(jìn)一步將數(shù)據(jù)進(jìn)行分割,最終直接返回我們的目標(biāo)數(shù)據(jù),進(jìn)一步說就是flatMap可以改造流,而map只能在流的基礎(chǔ)上處理。

        另外,需要注意的是,流和水管中的水一樣,一旦被處理之后(流過)就不存在了,所以是沒有辦法作為參數(shù)被頻繁使用。

        collect

        toMap

        collect我們前面的示例一直都在使用,和其他方法相比,collect就是流的終點(diǎn),也就是最終的收集器,collect除了可以把數(shù)據(jù)收集到List、Set中,還可以把數(shù)據(jù)處理成map

        User?user1?=?new?User(1L,?"syske");
        ????????User?user2?=?new?User(2L,?"yun?zhong?zhi");
        ????????List?userList?=?Lists.newArrayList(user1,?user2);
        //?構(gòu)建userId,userName?集合
        Map?collect6?=?userList.stream().collect(Collectors.toMap(User::getId,?User::getUsername));
        //?構(gòu)建userId,用戶集合
        Map?collect7?=?userList.stream().collect(Collectors.toMap(User::getId,?Function.identity()));

        需要注意的是,toMap需要避免key沖突,通常情況下我們只需要多加一個(gè)參數(shù)即可解決問題:

        Map?collect6?=?userList.stream().collect(Collectors.toMap(User::getId,?User::getUsername,?(a,?b)?->?a));

        這里的(a, b) -> a意思就是如果key已經(jīng)存在,則保留舊的值,這一點(diǎn)可以從源碼中看出來:

        如果舊的值為null,則新值直接覆蓋,否則根據(jù)我們的策略取值,即用舊值。

        groupBy

        除了上面的toMap,下面這個(gè)就更方便了,可以直接根據(jù)數(shù)據(jù)的某個(gè)屬性分組,最終返回屬性對應(yīng)的map,比如這里

        User?user1?=?new?User(1L,?"syske");
        User?user2?=?new?User(2L,?"yun?zhong?zhi");
        User?user3?=?new?User(2L,?"yun?zhong?zhi?2");
        List?userList?=?Lists.newArrayList(user1,?user2,?user3);
        Map>?collect8?=?userList.stream().collect(Collectors.groupingBy(User::getId));
        //?運(yùn)行結(jié)果如下:
        //?{1=[User{id=1,?username='syske'}],?2=[User{id=2,?username='yun?zhong?zhi'},?User{id=2,?username='yun?zhong?zhi?2'}]}

        除了上面這種,還有其他更方便更強(qiáng)的操作,比如我想分組之后統(tǒng)計(jì)數(shù)量:

        Map?collect9?=?userList.stream().collect(Collectors.groupingBy(User::getId,?Collectors.counting()));
        //?運(yùn)行結(jié)果如下(元數(shù)據(jù)同上):{1=1, 2=2}

        需要注意的是,Collectors.counting()返回的是Long。

        再比如分組之后我們還需要對某個(gè)字段求和:

        Map?collect10?=?userList.stream().collect(Collectors.groupingBy(User::getId,?Collectors.summingLong(User::getId)));
        //?運(yùn)行結(jié)果如下(元數(shù)據(jù)同上):{1=1, 2=4}

        再比如分組之后求平均值:

        Map?collect11?=?userList.stream().collect(Collectors.groupingBy(User::getId,?Collectors.averagingLong(User::getId)));
        //?運(yùn)行結(jié)果如下(元數(shù)據(jù)同上):{1=1.0, 2=2.0}

        好了,關(guān)于groupBy我們就說這么多,還有其他需求的小伙伴可以自己再研究下。

        其他

        collect這塊除了我們上面提到的,還有幾個(gè)比較常用的,其實(shí)就是我們在groupBy那塊組合用到的,比如求平均值:

        Double?collect12?=?userList.stream().collect(Collectors.averagingLong(User::getId));

        求和:

        Long?collect13?=?userList.stream().collect(Collectors.summingLong(User::getId));

        還有我們前面說的joining、maxByminBy、counting等,就不一一列舉了,因?yàn)榉椒▽?shí)在是太多了。

        結(jié)語

        應(yīng)該說從jdk1.8開始,lambdajava編程更優(yōu)雅也更簡便,但這并不是推薦你在日常開發(fā)中全部使用lambda表達(dá)式,畢竟在某些場景下,lambda性能并不好,關(guān)于這塊我們之前是有測試結(jié)果的:


        當(dāng)然如果用parallelStream會解決性能問題,但是在使用parallelStream的時(shí)候盡可能不要用到外部變量,否則會導(dǎo)致線程安全問題,這個(gè)我踩過坑??傊褪悄阋龑W(xué)會把握使用lambda的場景,在一些性能差別不是特別大的場景下,用lamdba會讓你的代碼更簡潔,更容易理解,最重要的是可以寫更少的代碼,提升開發(fā)效率。

        今天雖然分享的內(nèi)容有點(diǎn)多,但都是滿滿的干貨,是我最近一段時(shí)間工作中使用lambda的一點(diǎn)點(diǎn)總結(jié),希望可以真正幫到各位小伙伴,好了,晚安吧!

        - END -


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

        手機(jī)掃一掃分享

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

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            无码成人视频 | 美女一区二区三区 | 99久久久无码国产精品免费麻豆 | 欧美视频免费在线观看 | 一区二区免费视频 | 丰满寡妇的肉体hd | 人人干人人草 | 欧美性欲视频 | 性感逼逼| 翔田千里无码AV一区二区三区 |