Java8之Stream流(六)收集

Java8之Stream流(一)基礎(chǔ)體驗(yàn)
Java8之Stream流(二)關(guān)鍵知識(shí)點(diǎn)
Java8之Stream流(三)縮減操作
Java8之Stream流(四)并行流
Java8之Stream流(五)映射流
Java8之Stream流(七)流與迭代器

我們前面的五篇文章基本都是在說(shuō)將一個(gè)集合轉(zhuǎn)成一個(gè)流吗浩,然后對(duì)流進(jìn)行操作,其實(shí)這種操作是最多的,但有時(shí)候我們也是需要從流中收集起一些元素,并以集合的方式返回,我們把這種反向操作稱(chēng)為收集旦棉。流API也給我們提供了相應(yīng)的方法。

如何在流中使用收集功能少孝?

我們先看一看流API給我們提供的方法:

public interface Stream<T> extends BaseStream<T, Stream<T>> {
//...忽略那些不重要的東西
<R> R collect(Supplier<R> supplier,
                  BiConsumer<R, ? super T> accumulator,
                  BiConsumer<R, R> combiner);
<R, A> R collect(Collector<? super T, A, R> collector);
}

流API中給我們提供了兩種邑退,我給大家分析一下

<R, A> R collect(Collector<? super T, A, R> collector);

其中R指定結(jié)果的類(lèi)型竹宋,T指定了調(diào)用流的元素類(lèi)型。內(nèi)部積累的類(lèi)型由A指定地技。collectorFunc是一個(gè)收集器蜈七,指定收集過(guò)程如何執(zhí)行,collect()方法是一個(gè)終端方法莫矗。雖然我們基本上很少會(huì)用到自定義的collectorFunc,但是了為擴(kuò)展大家的知識(shí)面飒硅,我們還是簡(jiǎn)單地聊一聊Collector,Because it's my style!

Collector接口位于java.util.stream包中的聲明作谚,它的容顏是這樣的:

package java.util.stream;
public interface Collector<T, A, R> {
      Supplier<A> supplier();
      BiConsumer<A, T> accumulator();
      BinaryOperator<A> combiner();
      Function<A, R> finisher();
}

其中T三娩、A、R的含義和上面是一樣的其中R指定結(jié)果的類(lèi)型妹懒,T指定了調(diào)用流的元素類(lèi)型雀监。內(nèi)部積累的類(lèi)型由A指定。但是這一篇我們不實(shí)現(xiàn)他們眨唬,因?yàn)镴DK已經(jīng)給我們提供了很強(qiáng)大的方法了会前,他們位于java.util.stream下面的Collectors類(lèi),我們本篇也主要是使用Collectors來(lái)實(shí)現(xiàn)收集的功能匾竿。

Collectors類(lèi)是一個(gè)最終類(lèi)瓦宜,里面提供了大量的靜態(tài)的收集器方法,借助他岭妖,我們基本可以實(shí)現(xiàn)各種復(fù)雜的功能了临庇。我們來(lái)看一下toList和toSet方法:

public static <T>  Collector<T, ?, List<T>> toList()
public static <T> Collector<T, ?, Set<T>> toSet()

其中Collectors#toList()返回的收集器可以把流中元素收集到一個(gè)List中,Collectors#toSet()返回的收集器可以把流中的元素收集到一個(gè)Set中区转。比如:如果你想把元素收集到List中苔巨,你可以這樣用,steam.collect(Collectors.toList).接下來(lái)废离,我們把我們的王者榮耀團(tuán)隊(duì)經(jīng)濟(jì)例子修改一下侄泽,把明星玩家和當(dāng)前獲得的金幣數(shù)收集到一個(gè)List里面,把出場(chǎng)的英雄收集到一個(gè)Set里面:

#玩家使用的英雄以及當(dāng)前獲得的金幣數(shù)
public class HeroPlayerGold {
    /** 使用的英雄名字 */
    private String hero;
    /** 玩家的ID */
    private String player;
    /** 獲得的金幣數(shù) */
    private int gold;


    public HeroPlayerGold(String hero, String player, int gold) {
        this.hero = hero;
        this.player = player;
        this.gold = gold;
    }

    @Override
    public String toString() {
        return "HeroPlayerGold{" +
                "hero='" + hero + '\'' +
                ", player='" + player + '\'' +
                ", gold=" + gold +
                '}';
    }
//省略get/set
}
#出場(chǎng)的英雄
public class Hero {
    /** 使用的英雄名字 */
    private String hero;

    public Hero(String hero) {
        this.hero = hero;
    }

    @Override
    public String toString() {
        return "Hero{" +
                "hero='" + hero + '\'' +
                '}';
    }
//省略get/set
}
#測(cè)試類(lèi)
public class Main {
    public static void main(String[] args) {
        learnCollect();
    }

    private static void learnCollect() {
        List<HeroPlayerGold> lists = new ArrayList<>();
        lists.add(new HeroPlayerGold("蓋倫", "RNG-Letme", 100));
        lists.add(new HeroPlayerGold("諸葛亮", "RNG-Xiaohu", 300));
        lists.add(new HeroPlayerGold("露娜", "RNG-MLXG", 300));
        lists.add(new HeroPlayerGold("狄仁杰", "RNG-UZI", 500));
        lists.add(new HeroPlayerGold("牛頭", "RNG-Ming", 500));

        List<PlayerGold> playerGolds = lists.stream()
                .map(plary -> new PlayerGold(plary.getPlayer(), plary.getGold()))
                .collect(Collectors.toList());
        System.out.println("============PlayerGold begin==============");
        playerGolds.forEach(System.out::println);
        System.out.println("============PlayerGold end================\n");



        Set<Hero> heroes = lists.stream().map(player -> new Hero(player.getHero())).collect(Collectors.toSet());
        System.out.println("============Hero begin==============");
        heroes.forEach(System.out::println);
        System.out.println("============Hero end================");
    }
}

輸出的日志:

============PlayerGold begin==============
PlayerGold{player='RNG-Letme', gold=100}
PlayerGold{player='RNG-Xiaohu', gold=300}
PlayerGold{player='RNG-MLXG', gold=300}
PlayerGold{player='RNG-UZI', gold=500}
PlayerGold{player='RNG-Ming', gold=500}
============PlayerGold end================

============Hero begin==============
Hero{hero='露娜'}
Hero{hero='牛頭'}
Hero{hero='蓋倫'}
Hero{hero='狄仁杰'}
Hero{hero='諸葛亮'}
============Hero end================

看到這里蜻韭,大家有感受到流API的威力了嗎悼尾?提示一下,封裝一個(gè)工具類(lèi)肖方,然后結(jié)合一FastJson這種東西一起使用闺魏!是真的好用啊俯画!其實(shí)將數(shù)據(jù)從集合移到流中析桥,或者將數(shù)據(jù)從流移回集合的能力,是流API給我們提供的一個(gè)強(qiáng)大特性,因?yàn)檫@允許通過(guò)流來(lái)操作集合泡仗,然后把流重新打包成集合埋虹。此外,條件合適的時(shí)候娩怎,讓流操作并行發(fā)生搔课,提高效率。

接下來(lái)我們分析第二個(gè)方法截亦,

 <R> R collect(Supplier<R> supplier,
                  BiConsumer<R, ? super T> accumulator,
                  BiConsumer<R, R> combiner);

我們第二個(gè)版本的收集方法爬泥,主要是可以在收集的過(guò)程中,給予更多的控制崩瓤。其中supplier指定如何創(chuàng)建用于保存結(jié)果的對(duì)象袍啡,比如,要使用ArrayList作為結(jié)果的集合却桶,需要指定它的構(gòu)造函數(shù)葬馋,accumulator函數(shù)是將一個(gè)元素添加到結(jié)果中,而combiner函數(shù)合并兩個(gè)部分的結(jié)果肾扰。大家應(yīng)該發(fā)現(xiàn)了吧,他的工作方式和我們第三篇介紹縮減操作時(shí)的reduce方法是很像的蛋逾。它們都必須是無(wú)狀態(tài)和不干預(yù)的集晚,并且必須有關(guān)聯(lián)性,三個(gè)約束條件缺一不可区匣。

Supplier也是java.util.function包中的一個(gè)函數(shù)式接口:

@FunctionalInterface
public interface Supplier<T> {
    T get();
}

只有一個(gè)get()偷拔,并且是沒(méi)有參數(shù)的,在collect()方法返回一個(gè)R類(lèi)型的對(duì)象亏钩,并且get()方法返回一個(gè)指向集合的引用莲绰。

而accumulator,combiner的類(lèi)型是BiConsumer,他們也是java.util.function包中的一個(gè)函數(shù)式接口:

@FunctionalInterface
public interface BiConsumer<T, U> {
    void accept(T t, U u);
}

其中t,u執(zhí)行某種類(lèi)型的操作姑丑,對(duì)于accumulator來(lái)說(shuō)蛤签,t指定了目標(biāo)集合,u指定了要添加到該集合的元素栅哀。對(duì)于combiner來(lái)說(shuō)震肮,t和u指定的是兩個(gè)要被合并的集合。我們把前面的例子改變一下留拾,然后也詳細(xì)地說(shuō)一下戳晌,在沒(méi)有用lambda和使用lambda之后的區(qū)別:

這個(gè)是沒(méi)有使用lambda前的:

private static void learnCollect() {
        List<HeroPlayerGold> lists = new ArrayList<>();
        lists.add(new HeroPlayerGold("蓋倫", "RNG-Letme", 100));
        lists.add(new HeroPlayerGold("諸葛亮", "RNG-Xiaohu", 300));
        lists.add(new HeroPlayerGold("露娜", "RNG-MLXG", 300));
        lists.add(new HeroPlayerGold("狄仁杰", "RNG-UZI", 500));
        lists.add(new HeroPlayerGold("牛頭", "RNG-Ming", 500));


        lists.stream().collect(new Supplier<HashSet<HeroPlayerGold>>() {
                                   @Override
                                   public HashSet<HeroPlayerGold> get() {
                                       return new HashSet<>();
                                   }
                               },//第一個(gè)參數(shù)
                new BiConsumer<HashSet<HeroPlayerGold>, HeroPlayerGold>() {
                    @Override
                    public void accept(HashSet<HeroPlayerGold> heroPlayerGolds, HeroPlayerGold heroPlayerGold) {
                        heroPlayerGolds.add(heroPlayerGold);
                    }
                },//第二個(gè)參數(shù)
                new BiConsumer<HashSet<HeroPlayerGold>, HashSet<HeroPlayerGold>>() {
                    @Override
                    public void accept(HashSet<HeroPlayerGold> heroPlayerGolds, HashSet<HeroPlayerGold> heroPlayerGolds2) {
                        heroPlayerGolds.addAll(heroPlayerGolds2);
                    }
                }//第三個(gè)參數(shù)
        ).forEach(System.out::println);
    }

在沒(méi)有使用lambda前,雖然看起來(lái)的讓人眼花繚亂的痴柔,但不得不說(shuō)沦偎,他其實(shí)能幫助我們實(shí)現(xiàn)非常強(qiáng)大的功能,我們自定義的收集過(guò)程,全部都可以交給這個(gè)家伙豪嚎,我們用lambda整理一下:

private static void learnCollect() {
        List<HeroPlayerGold> lists = new ArrayList<>();
        lists.add(new HeroPlayerGold("蓋倫", "RNG-Letme", 100));
        lists.add(new HeroPlayerGold("諸葛亮", "RNG-Xiaohu", 300));
        lists.add(new HeroPlayerGold("露娜", "RNG-MLXG", 300));
        lists.add(new HeroPlayerGold("狄仁杰", "RNG-UZI", 500));
        lists.add(new HeroPlayerGold("牛頭", "RNG-Ming", 500));


        lists.stream().collect(() -> new HashSet<>(),
                                (set,elem)->set.add(elem),
                                (setA,setB)->setA.addAll(setB)
        ).forEach(System.out::println);
        
}

大家以為到這里就結(jié)束了嗎搔驼?其實(shí)還可以使用方法引用和構(gòu)造函數(shù)引用來(lái)簡(jiǎn)化:

private static void learnCollect() {
        List<HeroPlayerGold> lists = new ArrayList<>();
        lists.add(new HeroPlayerGold("蓋倫", "RNG-Letme", 100));
        lists.add(new HeroPlayerGold("諸葛亮", "RNG-Xiaohu", 300));
        lists.add(new HeroPlayerGold("露娜", "RNG-MLXG", 300));
        lists.add(new HeroPlayerGold("狄仁杰", "RNG-UZI", 500));
        lists.add(new HeroPlayerGold("牛頭", "RNG-Ming", 500));


        lists.stream().collect(HashSet::new,
                               HashSet::add,
                               HashSet::addAll
        ).forEach(System.out::println);
}

小結(jié)一下

本篇帶大家入門(mén)了Stream的收集操作,但是有了些這入門(mén)操作疙渣,我相信匙奴,你在我的演變過(guò)程中已經(jīng)發(fā)現(xiàn)了擴(kuò)展點(diǎn)了,不管是supplier妄荔,accumulator還是combiner泼菌,都可以在里面放一些特別的操作進(jìn)去,從而滿(mǎn)足你們的各種要求啦租。另外一個(gè)點(diǎn)哗伯,大家一定不要忘記了Collectors這個(gè)最終類(lèi),里面已經(jīng)提供了很多很強(qiáng)大的靜態(tài)方法篷角,如果你們遇到一些特別的需求焊刹,首先要想到的應(yīng)該是Collectors,如果里面的方法都不能實(shí)現(xiàn)你的要求恳蹲,再考慮通過(guò)第二個(gè)版本的collect()方法實(shí)現(xiàn)你的自定義收集過(guò)程吧虐块。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市嘉蕾,隨后出現(xiàn)的幾起案子贺奠,更是在濱河造成了極大的恐慌,老刑警劉巖错忱,帶你破解...
    沈念sama閱讀 217,542評(píng)論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件儡率,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡以清,警方通過(guò)查閱死者的電腦和手機(jī)儿普,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,822評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)掷倔,“玉大人眉孩,你說(shuō)我怎么就攤上這事〗衲В” “怎么了勺像?”我有些...
    開(kāi)封第一講書(shū)人閱讀 163,912評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)错森。 經(jīng)常有香客問(wèn)我吟宦,道長(zhǎng),這世上最難降的妖魔是什么涩维? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,449評(píng)論 1 293
  • 正文 為了忘掉前任殃姓,我火速辦了婚禮袁波,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘蜗侈。我一直安慰自己篷牌,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,500評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布踏幻。 她就那樣靜靜地躺著枷颊,像睡著了一般。 火紅的嫁衣襯著肌膚如雪该面。 梳的紋絲不亂的頭發(fā)上夭苗,一...
    開(kāi)封第一講書(shū)人閱讀 51,370評(píng)論 1 302
  • 那天,我揣著相機(jī)與錄音隔缀,去河邊找鬼题造。 笑死,一個(gè)胖子當(dāng)著我的面吹牛猾瘸,可吹牛的內(nèi)容都是我干的界赔。 我是一名探鬼主播,決...
    沈念sama閱讀 40,193評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼牵触,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼淮悼!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起揽思,我...
    開(kāi)封第一講書(shū)人閱讀 39,074評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤敛惊,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后绰更,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,505評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡锡宋,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,722評(píng)論 3 335
  • 正文 我和宋清朗相戀三年儡湾,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片执俩。...
    茶點(diǎn)故事閱讀 39,841評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡徐钠,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出役首,到底是詐尸還是另有隱情尝丐,我是刑警寧澤,帶...
    沈念sama閱讀 35,569評(píng)論 5 345
  • 正文 年R本政府宣布衡奥,位于F島的核電站爹袁,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏矮固。R本人自食惡果不足惜失息,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,168評(píng)論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧盹兢,春花似錦邻梆、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,783評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至见芹,卻和暖如春剂娄,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背辆童。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,918評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工宜咒, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人把鉴。 一個(gè)月前我還...
    沈念sama閱讀 47,962評(píng)論 2 370
  • 正文 我出身青樓故黑,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親庭砍。 傳聞我的和親對(duì)象是個(gè)殘疾皇子场晶,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,781評(píng)論 2 354

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

  • Java流庫(kù)(java.util.stream) 流提供了一種讓我們可以在比集合更高的概念級(jí)別上指定計(jì)算的數(shù)據(jù)視圖...
    thorhill閱讀 4,840評(píng)論 0 4
  • 原文地址 http://blog.csdn.net/myherux/article/details/7185511...
    Vissioon閱讀 678評(píng)論 0 0
  • Int Double Long 設(shè)置特定的stream類(lèi)型, 提高性能怠缸,增加特定的函數(shù) 無(wú)存儲(chǔ)诗轻。stream不是一...
    patrick002閱讀 1,273評(píng)論 0 0
  • Java8 in action 沒(méi)有共享的可變數(shù)據(jù),將方法和函數(shù)即代碼傳遞給其他方法的能力就是我們平常所說(shuō)的函數(shù)式...
    鐵牛很鐵閱讀 1,229評(píng)論 1 2
  • 收集器簡(jiǎn)介 Collector 函數(shù)式編程相對(duì)于指令式編程的一個(gè)主要優(yōu)勢(shì):你只需要指出希望的結(jié)果“做什么”揭北,而不用...
    潯它芉咟渡閱讀 818評(píng)論 0 4