Java8特性④Stream收集數據

收集器可以簡潔而靈活地定義collect用來生成結果集合的標準。更具體地說,對流調用 collect 方法將對流中的元素觸發(fā)一個歸約操作(由Collector來參數化)。一般來說,Collector 會對元素應用一個轉換函數(很多時候是不體現(xiàn)任何效果的恒等轉換, 例如 toList ),并將結果累積在一個數據結構中,從而產生這一過程的最終輸出。下面就來學習那些可以從Collectors 類提供的工廠方法(例如groupingBy)創(chuàng)建的收集器。

歸約和匯總

查找流中的最大值和最小值

Collectors.maxBy 和 Collectors.minBy 來計算流中的最大或最小值。

Optional<Dish> maxDish = Dish.menu.stream().
      collect(Collectors.maxBy(Comparator.comparing(Dish::getCalories)));
maxDish.ifPresent(System.out::println);

Optional<Dish> minDish = Dish.menu.stream().
      collect(Collectors.minBy(Comparator.comparing(Dish::getCalories)));
minDish.ifPresent(System.out::println);

匯總

Collectors.summingInt 匯總求和;
Collectors.averagingInt 匯總求平均值;
Collectors.summarizingInt 匯總所有信息包括數量、求和、平均值、最小值、最大值;

//求總熱量
int totalColories = Dish.menu.stream().collect(Collectors.summingInt(Dish::getCalories));
System.out.println(totalColories);

//求平均熱量
double averageColories = Dish.menu.stream().collect(Collectors.averagingInt(Dish::getCalories));
System.out.println(averageColories);

//匯總
IntSummaryStatistics menuStatistics = Dish.menu.stream().collect(Collectors.summarizingInt(Dish::getCalories));
System.out.println(menuStatistics);
IntSummaryStatistics{count=9, sum=4300, min=120, average=477.777778, max=800}

連接字符串

joining 工廠方法返回的收集器會把對流中每一個對象應用toString方法得到的所有字符串連接成一個字符串。

String menu = Dish.menu.stream().map(Dish::getName).collect(Collectors.joining(","));
System.out.println(menu);
//pork,beef,chicken,french fries,rice,season fruit,pizza,prawns,salmon

Collectors.reducing

Collectors.reducing 工廠方法是上面所有工廠方法的一般情況,它完全可以實現(xiàn)上述方法的功能。它需要三個參數:

  • 第一個參數是歸約操作的起始值,也是流中沒有元素時的返回值,所以很顯然對于數值和而言0是一個合適的值。
  • 第二個參數是一個 Function,就是具體的取值函數。
  • 第三個參數是一個 BinaryOperator,將兩個項目累積成一個同類型的值。。
int totalCalories = Dish.menu.stream().collect(Collectors.reducing( 0, Dish::getCalories, (i, j) -> i + j));

分組

用Collectors.groupingBy工廠方法返回的收集器可以實現(xiàn)分組任務,分組操作的結果是一個Map,把分組函數返回的值作為映射的鍵,把流中 所有具有這個分類值的項目的列表作為對應的映射值。

多級分組

//Dish的Type為鍵,Dish類型所對應的dish集合為值
Map<Dish.Type, List<Dish>> dishesByType = Dish.menu.stream().collect(Collectors.groupingBy(Dish::getType));
System.out.println(dishesByType);
//{FISH=[prawns, salmon], OTHER=[french fries, rice, season fruit, pizza], MEAT=[pork, beef, chicken]}

//一級分類為Dish的Type,二級分類為Dish的CaloricLevel
Map<Dish.Type, Map<Dish.CaloricLevel, List<Dish>>> dishes = Dish.menu.stream()
      .collect(Collectors.groupingBy(Dish::getType, Collectors.groupingBy(Dish::getLevel)));
System.out.println(dishes);
//{FISH={NORMAL=[salmon], DIET=[prawns]}, OTHER={NORMAL=[french fries, pizza], DIET=[rice, season fruit]}, MEAT={NORMAL=[beef], FAT=[pork], DIET=[chicken]}}

按子集收集數據

//Dish的Type為鍵,Dish類型所對應的dish集合的size為值
Map<Dish.Type, Long> dishTypeCount = Dish.menu.stream().collect(Collectors.groupingBy(Dish::getType, Collectors.counting()));
System.out.println(dishTypeCount);
//{FISH=2, OTHER=4, MEAT=3}

分區(qū)

分區(qū)是分組的特殊情況:由一個謂詞(返回一個布爾值的函數)作為分類函數,它稱分區(qū)函數。分區(qū)函數返回一個布爾值,這意味著得到的分組 Map 的鍵類型是 Boolean,于是它最多可以分為兩組——true是一組,false是一組。分區(qū)的好處在于保留了分區(qū)函數返回true或false的兩套流元素列表。

Map<Boolean, Map<Dish.Type, List<Dish>>> partitioningDish = Dish.menu.stream().collect(Collectors.partitioningBy(Dish::isVegetarian, Collectors.groupingBy(Dish::getType)));
System.out.println(partitioningDish);
//false={FISH=[prawns, salmon], MEAT=[pork, beef, chicken]}, 
//true={OTHER=[french fries, rice, season fruit, pizza]}

小結

下表展示 Collectors 類的靜態(tài)工廠方法。

工廠方法 返回類型 作用
toList List<T> 把流中所有項目收集到一個 List
toSet Set<T> 把流中所有項目收集到一個 Set,刪除重復項
toCollection Collection<T> 把流中所有項目收集到給定的供應源創(chuàng)建的集合menuStream.collect(toCollection(), ArrayList::new)
counting Long 計算流中元素的個數
sumInt Integer 對流中項目的一個整數屬性求和
averagingInt Double 計算流中項目 Integer 屬性的平均值
summarizingInt IntSummaryStatistics 收集關于流中項目 Integer 屬性的統(tǒng)計值,例如最大、最小、 總和與平均值
joining String 連接對流中每個項目調用 toString 方法所生成的字符串collect(joining(", "))
maxBy Optional<T> 一個包裹了流中按照給定比較器選出的最大元素的 Optional, 或如果流為空則為 Optional.empty()
minBy Optional<T> 一個包裹了流中按照給定比較器選出的最小元素的 Optional, 或如果流為空則為 Optional.empty()
reducing 歸約操作產生的類型 從一個作為累加器的初始值開始,利用 BinaryOperator 與流 中的元素逐個結合,從而將流歸約為單個值累加int totalCalories = menuStream.collect(reducing(0, Dish::getCalories, Integer::sum));
collectingAndThen 轉換函數返回的類型 包裹另一個收集器,對其結果應用轉換函數int howManyDishes = menuStream.collect(collectingAndThen(toList(), List::size))
groupingBy Map<K, List<T>> 根據項目的一個屬性的值對流中的項目作問組,并將屬性值作 為結果 Map 的鍵
partitioningBy Map<Boolean,List<T>> 根據對流中每個項目應用謂詞的結果來對項目進行分區(qū)

附錄:Dish類

package com.company.bean;

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

/**
 * Created by liuguoquan on 2017/4/26.
 */
public class Dish {

    private String name;
    private boolean vegetarian;
    private int calories;
    private Type type;
    private CaloricLevel level;

    public CaloricLevel getLevel() {

        if (calories <= 400) {

            return CaloricLevel.DIET;
        } else if (calories <= 700) {

            return CaloricLevel.NORMAL;
        }
        return CaloricLevel.FAT;
    }

    public void setLevel(CaloricLevel level) {
        this.level = level;
    }

    public enum Type { MEAT, FISH, OTHER }
    public enum CaloricLevel { DIET, NORMAL, FAT }

    public Dish(String name, boolean vegetarian, int calories, Type type) {
        this.name = name;
        this.vegetarian = vegetarian;
        this.calories = calories;
        this.type = type;
    }

    public String getName() {
        return name;
    }

    public boolean isVegetarian() {
        return vegetarian;
    }

    public int getCalories() {
        return calories;
    }

    public Type getType() {
        return type;
    }

    @Override
    public String toString() {
        return name;
    }

    public static final List<Dish> menu =
            Arrays.asList( new Dish("pork", false, 800, Dish.Type.MEAT),
                    new Dish("beef", false, 700, Dish.Type.MEAT),
                    new Dish("chicken", false, 400, Dish.Type.MEAT),
                    new Dish("french fries", true, 530, Dish.Type.OTHER),
                    new Dish("rice", true, 350, Dish.Type.OTHER),
                    new Dish("season fruit", true, 120, Dish.Type.OTHER),
                    new Dish("pizza", true, 550, Dish.Type.OTHER),
                    new Dish("prawns", false, 400, Dish.Type.FISH),
                    new Dish("salmon", false, 450, Dish.Type.FISH));
}
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,572評論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,071評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,409評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,569評論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,360評論 6 404
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 54,895評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 42,979評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,123評論 0 286
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發(fā)現(xiàn)了一具尸體,經...
    沈念sama閱讀 48,643評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,559評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,742評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,250評論 5 356
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 43,981評論 3 346
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,363評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,622評論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,354評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,707評論 2 370

推薦閱讀更多精彩內容

  • 收集器簡介 Collector 函數式編程相對于指令式編程的一個主要優(yōu)勢:你只需要指出希望的結果“做什么”,而不用...
    潯它芉咟渡閱讀 828評論 0 4
  • Java8 in action 沒有共享的可變數據,將方法和函數即代碼傳遞給其他方法的能力就是我們平常所說的函數式...
    鐵牛很鐵閱讀 1,249評論 1 2
  • 收集器簡介 匯總 并行流 歡迎訪問本人博客查看原文:http://wangnan.tech 收集器簡介 對流調用c...
    GhostStories閱讀 1,471評論 1 7
  • Spring Cloud為開發(fā)人員提供了快速構建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 134,776評論 18 139
  • Int Double Long 設置特定的stream類型, 提高性能,增加特定的函數 無存儲。stream不是一...
    patrick002閱讀 1,280評論 0 0