Java菜譜(二)——怎么求男學(xué)生的平均分?

今天的場景設(shè)計(jì)是這樣的:

給定一批學(xué)生分?jǐn)?shù)的數(shù)據(jù),求出所有男學(xué)生的平均分?jǐn)?shù)谁不。

如果這個命題放在sql中,應(yīng)該是送分題漾峡。在Java中去實(shí)現(xiàn)深夯,可能也沒有那么難。但是當(dāng)場景不斷復(fù)雜化乃正,我們就需要一些技巧來解決這類問題了住册。

假設(shè)Student類的數(shù)據(jù)結(jié)構(gòu)如下:

@Data
public class Student {
    /**
     * 學(xué)生ID
     */
    private String id;
    /**
     * 學(xué)生姓名
     */
    private String name;
    /**
     * 學(xué)生年齡
     */
    private Integer age;
    /**
     * 學(xué)生性別 0-女 1-男
     */
    private Integer gender;
    /**
     * 學(xué)生成績
     */
    private Double score;
}

傳統(tǒng)思路

假設(shè)學(xué)生的數(shù)據(jù)是以List<Student>的形式給出的,讓我們先來回顧一下傳統(tǒng)思路是怎么解決這個問題的瓮具,由于

平均分?jǐn)?shù)=總分/人數(shù)

因此荧飞,我們需要一個臨時變量去記錄總分,另一個臨時變量去記錄男學(xué)生的人數(shù)名党,然后我們遍歷學(xué)生的列表叹阔,如果遍歷到的學(xué)生為男學(xué)生,則總分加上當(dāng)前學(xué)生的分?jǐn)?shù)传睹,人數(shù)加1耳幢,相關(guān)代碼如下:

    Double totalScore = 0.0;
    int count = 0;
    for (Student student : students) {
        // 男學(xué)生
        if (student.getGender() == 1) {
            totalScore += student.getScore();
            count++;
        }
    }
    Double average = totalScore / count;
    System.out.println(average);

這樣的思路屬于命令式編程的范式,即我們一步一步告訴計(jì)算機(jī)先做什么再做什么欧啤,其好處是邏輯簡單睛藻,容易理解和編寫,也容易調(diào)試邢隧。但是這樣的方式編程通常代碼量巨大修档,并且很容易編寫出執(zhí)行效率低下的代碼,處理復(fù)雜邏輯時更是容易丟掉代碼的可讀性府框。

Stream流式計(jì)算

在Jdk8以后吱窝,Java引入了lambda表達(dá)式讥邻,使得Java可以更方便地使用函數(shù)式的風(fēng)格編寫程序。而同一版本中Stream的引入更是極大簡化了集合的操作院峡。

那么就讓我們來看一下在Stream的幫助下如何解決上面的問題:

    Double average = students.stream()
                .filter(s -> s.getGender() == 1)
                .collect(Collectors.averagingDouble(Student::getScore));

     System.out.println(average);

首先通過列表的stream()方法將列表轉(zhuǎn)為流兴使,再通過filter方法對流中的元素進(jìn)行過濾,最后通過collect方法對流中的元素進(jìn)行歸并照激,得到最終的結(jié)果发魄。事實(shí)上,所有使用流的場景都遵循這三個步驟俩垃,即流的創(chuàng)建励幼、流的轉(zhuǎn)換以及流的歸并。

上述流式計(jì)算的方式是一種函數(shù)式編程的風(fēng)格口柳,同時也是屬于聲明式編程的范式苹粟。相比于命令式編程,聲明式編程更強(qiáng)調(diào)告訴計(jì)算機(jī)要做什么跃闹,而不是具體怎么做嵌削。每個步驟具體的實(shí)現(xiàn)方案由計(jì)算機(jī)內(nèi)部自行實(shí)現(xiàn)。當(dāng)然望艺,這也依賴于Jdk內(nèi)部提供的強(qiáng)大的api苛秕。

更復(fù)雜的場景

讓我們把場景變得更復(fù)雜一些,來見識一樣流式計(jì)算的威力找默。

復(fù)雜場景1:學(xué)生分屬于不同班艇劫,計(jì)算每個班男同學(xué)的平均分

學(xué)生的類增加相應(yīng)字段,改造為:

@Builder
@Data
public class Student {
    /**
     * 學(xué)生ID
     */
    private String id;
    /**
     * 學(xué)生姓名
     */
    private String name;
    /**
     * 學(xué)生年齡
     */
    private Integer age;
    /**
     * 學(xué)生性別 0-女 1-男
     */
    private Integer gender;
    /**
     * 學(xué)生成績
     */
    private Double score;
    /**
     * 學(xué)生屬于哪個班
     */
    private Integer classNumber;
}

上述需求實(shí)現(xiàn)代碼如下:

final Map<Integer, Double> averageMap = students.stream()
                .filter(s -> s.getGender() == 1)
                .collect(Collectors.groupingBy(Student::getClassNumber, 
                    Collectors.averagingDouble(Student::getScore)));
        System.out.println(averageMap);

由于需要每個班的成績惩激,我們對學(xué)生按班級進(jìn)行分組港准,使用的是Collectors工具類提供的groupingBy()方法。這個方法第一個參數(shù)是分類的依據(jù)咧欣,這里傳的是Student::getClassNumber這個方法引用浅缸,即怎么根據(jù)學(xué)生對象獲取到學(xué)生的班級。第二個參數(shù)傳的是下游的收集器魄咕,即分組之后對每組元素做怎樣的操作衩椒,這里和之前一樣傳的是對學(xué)生的成績?nèi)∑骄值牟僮鳌H绻覀冎粚?shù)據(jù)進(jìn)行分組哮兰,不進(jìn)行后續(xù)處理毛萌,第二個參數(shù)可以不傳(重載方法)。

復(fù)雜場景2:計(jì)算分?jǐn)?shù)高于平均分的學(xué)生人數(shù)

    // 先求平均分
    final Double average = students.stream()
        .collect(Collectors.averagingDouble(Student::getScore));

    // 再求超過平均分的人數(shù)
    final long count = students.stream()
        .filter(s -> s.getScore() > average)
        .count();
    System.out.println(count);

這個需求想整合成一次流式操作比較困難喝滞,我們需要先獲取班級的平均分阁将,再去計(jì)算分?jǐn)?shù)超過平均分的人數(shù)。需要注意的是右遭,Stream對象是“一次性的”做盅,當(dāng)一次歸并操作完成后缤削,Stream就會被關(guān)閉,這時如果復(fù)用之前的對象就會拋出異常吹榴。

這里只舉這兩個例子亭敢,Stream還有很多方便的API,感興趣的可以自行嘗試图筹∷У叮總結(jié)一下,使用Stream可以極大簡化集合相關(guān)的操作远剩,如果有相關(guān)的數(shù)據(jù)處理需求扣溺,可以嘗試使用。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末瓜晤,一起剝皮案震驚了整個濱河市锥余,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌活鹰,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,539評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件只估,死亡現(xiàn)場離奇詭異志群,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)蛔钙,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,594評論 3 396
  • 文/潘曉璐 我一進(jìn)店門锌云,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人吁脱,你說我怎么就攤上這事桑涎。” “怎么了兼贡?”我有些...
    開封第一講書人閱讀 165,871評論 0 356
  • 文/不壞的土叔 我叫張陵攻冷,是天一觀的道長。 經(jīng)常有香客問我遍希,道長等曼,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,963評論 1 295
  • 正文 為了忘掉前任凿蒜,我火速辦了婚禮禁谦,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘废封。我一直安慰自己州泊,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,984評論 6 393
  • 文/花漫 我一把揭開白布漂洋。 她就那樣靜靜地躺著遥皂,像睡著了一般力喷。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上渴肉,一...
    開封第一講書人閱讀 51,763評論 1 307
  • 那天冗懦,我揣著相機(jī)與錄音,去河邊找鬼仇祭。 笑死披蕉,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的乌奇。 我是一名探鬼主播没讲,決...
    沈念sama閱讀 40,468評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼礁苗!你這毒婦竟也來了爬凑?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,357評論 0 276
  • 序言:老撾萬榮一對情侶失蹤试伙,失蹤者是張志新(化名)和其女友劉穎嘁信,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體疏叨,經(jīng)...
    沈念sama閱讀 45,850評論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡潘靖,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,002評論 3 338
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了蚤蔓。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片卦溢。...
    茶點(diǎn)故事閱讀 40,144評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖秀又,靈堂內(nèi)的尸體忽然破棺而出单寂,到底是詐尸還是另有隱情,我是刑警寧澤吐辙,帶...
    沈念sama閱讀 35,823評論 5 346
  • 正文 年R本政府宣布宣决,位于F島的核電站,受9級特大地震影響昏苏,放射性物質(zhì)發(fā)生泄漏疲扎。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,483評論 3 331
  • 文/蒙蒙 一捷雕、第九天 我趴在偏房一處隱蔽的房頂上張望椒丧。 院中可真熱鬧,春花似錦救巷、人聲如沸壶熏。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,026評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽棒假。三九已至溯职,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間帽哑,已是汗流浹背谜酒。 一陣腳步聲響...
    開封第一講書人閱讀 33,150評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留妻枕,地道東北人僻族。 一個月前我還...
    沈念sama閱讀 48,415評論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像屡谐,于是被迫代替她去往敵國和親述么。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,092評論 2 355

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