自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

stream的實(shí)用方法和注意事項(xiàng)

開發(fā)
相信大家一定都在項(xiàng)目開發(fā)中享受過stream帶來的便利性和優(yōu)雅的代碼風(fēng)格。接下來補(bǔ)充幾個項(xiàng)目中不常見到但是同樣實(shí)用的api,同時跟大家一起探討stream這把雙刃劍的另一面。

使用但不常見的方法

filter、map、skip等方法想必大家都十分熟悉 無需贅述。這里僅介紹工程中使用較少但同樣實(shí)用的方法。

?  reduce

reduce有3個參數(shù):初始值、累加器、組合器。下面通過幾個case為大家逐一講解。由于比較繞,下面貼上ide執(zhí)行結(jié)果。

圖片

當(dāng)順序讀流或者累加器的參數(shù)和它的實(shí)現(xiàn)的類型匹配時,我們不需要使用組合器。通常只有在處理對象屬性時則需要組合器來幫助編譯器推斷入?yún)㈩愋?。?shí)際在串行流中組合器并不會實(shí)際執(zhí)行,只需要出入?yún)㈩愋蜐M足編譯器推斷要求即可??梢钥吹缴戏絩esult3的計算,末尾組合器適用max還是min 結(jié)果是一樣的。

?  allMatch/anyMatch/noneMatch

判斷集合中是否 全部都匹配/存在任意匹配/不存在匹配 某一規(guī)則。

比如下面一段代碼,判斷集合中的對象是否全部合法。語義十分簡單。下面對比stream寫法和常規(guī)寫法。兩種寫法的運(yùn)行結(jié)果是一樣的。

@Data

@AllArgsConstructor

public static class Calendar {

    private LocalDate date;

    private boolean today;

    private boolean signed;

}

//日歷初始化

LocalDate now = new LocalDate();

List<Calendar> calendars = Arrays.asList(

    new Calendar(new LocalDate(1661174238000L), false, false)

    , new Calendar(new LocalDate(1661828371000L), false, false)

    , new Calendar(new LocalDate(1661433438000L), false, false)

    , new Calendar(new LocalDate(1661519838000L), false, false)

    , new Calendar(new LocalDate(1661779038000L), false, false)

    , new Calendar(now, true, true)

);

//判斷昨天是否簽到過。寫法一

boolean yesterdaySigned = calendars.stream()

    .anyMatch(

        t -> Days.daysBetween(t.getDate(), now).getDays() == 1 && t.isSigned()

    );

System.out.println("昨天是否簽到過 -> " + yesterdaySigned);


//寫法二

boolean yesterdaySigned2 = false;

for (Calendar calendar : calendars) {

    if (Days.daysBetween(calendar.getDate(), now).getDays() == 1) {

        //找到昨天的日歷,并判斷是否簽到

        yesterdaySigned2 = calendar.isSigned();

        break;

    }

}

System.out.println("昨天是否簽到過寫法二 -> " + yesterdaySigned2);

這里寫法一雖然更簡練但是存在問題,大家有看出來的嗎。這個問題放在“注意事項(xiàng)”中專門講解。

?  flatMap

跟map的區(qū)別是可以將一個對象轉(zhuǎn)化成多個對象并以流的方式返回,適合用于集合嵌套場景下的扁平化處理。概念較為拗口,以下用ide截圖演示??梢钥吹教囟▓鼍跋耭latmap相對map有先天優(yōu)勢。

圖片

注意事項(xiàng)

?  書寫順序影響性能

stream實(shí)際使用中,filter和map最為常見。這兩個操作都是逐個元素執(zhí)行并逐個向下游操作傳遞,我們稱之為“垂直操作”(補(bǔ)充:sorted是“水平操作”,即會截斷后續(xù)運(yùn)算直至自己將流中所有元素操作完成)。其中filter較為特殊,被其攔截后不會繼續(xù)向下游傳遞?;诖嗽?,盡可能將filter前置往往可以大幅提高stream操作性能。如下所示:

圖片

一個長度為5的字符集,map-filter-foreach 順序執(zhí)行 則會有5次map、5次filter、1次foreach;filter-map-foreach順序執(zhí)行,則會有5次filter、1次map、1次foreach執(zhí)行。并且很容易推斷filter過濾度越高性能差異就會越明顯。

原理不少人可能會覺得簡單易懂,但遺憾的是在大型項(xiàng)目中往往總能找到有此類性能缺陷的代碼,諸如:

List<Long> awardId = timeFilterAwardConfigs.stream()

            .map(config -> config.getAwardId())

            .filter(awardId -> awardId > 0)

            .collect(Collectors.toList());

但在更復(fù)雜的場景下,也并非要求filter無腦提前于其他操作。比如下面這個例子:

//假設(shè)一份用戶集

        List<User> userList = Arrays.asList(

            new User("張三", 22)

            , new User("李四", 21)

            , new User("王五", 19)

            , new User("趙六", 25)

        );

        //要輸出這份集合中所有用戶所就職的公司的年度營業(yè)額總和,要求公司所在地都在杭州市余杭區(qū)

        // 注意用戶中可能有無業(yè)游民。不考慮就職公司重合或者一人就職多家公司的情況。

        //寫法一

        int allCompanyTurnover1 = userList.stream()

            .map(user -> calculateAnnualTurnover(queryUserCompany(user)))

            .filter(Objects::nonNull)

            .reduce(0, Integer::sum);

        //寫法二

        int allCompanyTurnover2 = userList.stream()

            .filter(user -> {

                Company company = queryUserCompany(user);

                return company != null && !"余杭".equals(company.getLocal());

            })

            .map(user -> calculateAnnualTurnover(queryUserCompany(user)))

            .reduce(0, Integer::sum);

寫法一顯然更符合直覺,寫法二雖然filter提前過濾掉了一部分?jǐn)?shù)據(jù),但是queryUserCompany存在重復(fù)計算。所以此種情況下就需要綜合 filter過濾度和queryUserCompany重復(fù)計算的開銷進(jìn)行權(quán)衡。如果filter過濾度足夠高(比如余杭的公司很少)同時queryUserCompany 資源開銷不大,那么寫法二更優(yōu),反之寫法一更優(yōu)。

?  并非適用所有場景

  • 性能上

這里就可以說回到剛才講anyMatch時看到的那段代碼:

//判斷昨天是否簽到過。寫法一

boolean yesterdaySigned = calendars.stream()

    .anyMatch(

        t -> Days.daysBetween(t.getDate(), now).getDays() == 1 && t.isSigned()

    );

System.out.println("昨天是否簽到過 -> " + yesterdaySigned);


//寫法二

boolean yesterdaySigned2 = false;

for (Calendar calendar : calendars) {

    if (Days.daysBetween(calendar.getDate(), now).getDays() == 1) {

        //找到昨天的日歷,并判斷是否簽到

        yesterdaySigned2 = calendar.isSigned();

        break;

    }

}

System.out.println("昨天是否簽到過寫法二 -> " + yesterdaySigned2);

打印觀察執(zhí)行次數(shù)如下:

圖片

顯然 anyMatch 會無條件遍歷所有元素再返回,而直觀的遍歷寫法往往不會犯這種錯誤,拿到結(jié)果后可以提前break。大家可能會想到先利用filter過濾獲獲取“昨天”的日歷,然后再anymatch。

boolean yesterdaySigned = calendars.stream()

            .filter(t -> Days.daysBetween(t.getDate(), now).getDays() == 1)

            .anyMatch(Calendar::isSigned);

但是很可惜,filter同樣會完整遍歷整個集合。事實(shí)上遍觀所有stream方法似乎都沒有辦法很好的解決這個問題。也歡迎大家一起探討。

  • 可閱讀性

摘取了某業(yè)務(wù)中判斷周期內(nèi)簽到次數(shù)的方法,采用stream和for循環(huán)常規(guī)寫法。

private int getCycleActionCount(Date start, Date end, List<ActionCalendar> calendar) {
        int count = 0;

        for (ActionCalendar calendarDay : calendar) {

            Date date = calendarDay.getDate();

            if (date.after(start) && date.before(end) && calendarDay.isComplete()) {

                //在周期內(nèi)任意一天簽到,簽到次數(shù)自增。

                count++;

            }

        }

        return count;

    }


    private int getCycleActionCount2(Date start, Date end, List<ActionCalendar> calendar) {

        return Math.toIntExact(

            calendar.stream()

                .filter(

                    //統(tǒng)計周期內(nèi)簽到天數(shù)

                    t -> (

                      t.getDate().after(start) && t.getDate().before(end) && t.isComplete()

                    )

                ).count()

        );

    }

這樣看兩者之間 光從可閱讀性上看并沒有特別大的區(qū)分度。而即使熟練的stream 愛好者,相信寫出一段stream代碼后也會多看幾眼確認(rèn)性能、縮進(jìn)是否達(dá)到最優(yōu)??梢娫谀承﹫鼍跋聼o論性能、可讀性還是書寫便利性都不占優(yōu),此時stream似乎就不是最優(yōu)選擇了。

總結(jié)

stream在多數(shù)場景下都能幫助我們更快的寫出優(yōu)美的代碼,但是在更為復(fù)雜的場景下則需要對API之間的執(zhí)行順序、lambda表達(dá)式的使用、甚至此場景是否適用stream寫法進(jìn)行一定的思考,以避免出現(xiàn)性能或可讀性的缺陷。

總的來看stream和直觀的for遍歷是互補(bǔ)而非替代關(guān)系,兩者搭配,干活不累。

此外stream家族中還有個強(qiáng)大的種子選手“parallelStream”(并行流)沒有介紹。他通常用在超大集合的處理中,日常工程中難尋使用場景,同時使用上比上面說到的串行流處理有更多的注意事項(xiàng)。這里暫不展開分享。

責(zé)任編輯:張燕妮 來源: 大淘寶技術(shù)
相關(guān)推薦

2011-06-23 11:15:25

SEO網(wǎng)站優(yōu)化

2010-08-12 09:39:26

FlexaddChil

2011-05-26 11:22:04

SEO

2009-06-11 17:52:08

JavaBean

2009-06-25 14:41:06

JavaBean

2025-01-09 08:49:36

Java并發(fā)編程

2011-06-24 09:23:02

SEO

2009-12-03 14:37:47

安裝phpMyAdmi

2009-06-12 09:46:40

Java String

2009-12-15 17:47:17

VSIP

2009-04-09 10:11:00

TCPIP設(shè)置

2021-12-20 23:22:46

Java開發(fā)升級

2024-11-29 14:50:45

2024-02-01 09:39:02

asyncawaitPromise

2020-10-20 14:05:48

用戶需求分析IT

2021-11-16 10:35:59

云計算云計算環(huán)境云應(yīng)用

2010-11-26 16:27:01

MySQL使用變量

2023-01-14 09:49:11

2011-09-26 11:02:10

2023-11-08 17:19:21

平臺工程架構(gòu)設(shè)計
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號