Java8 stream流操作(下)

本文是我在學(xué)習(xí)Java8的時候參考下面大佬的文章拷貝的,僅用于個人整理和學(xué)習(xí)用途
原文地址:[http://www.reibang.com/p/e429c517e9cb]

本篇文章我們繼續(xù)講解流的其他操作
沒有看過上篇文章的可以先點擊進去學(xué)習(xí)一下 Java8 stream流操作(上)

本篇文章主要內(nèi)容:

一種特化形式的流——數(shù)值流

  • Optional 類
  • 如何構(gòu)建一個流
  • collect 方法
  • 并行流相關(guān)問題

一. 數(shù)值流

前面介紹的如
int sum = list.stream().map(Person::getAge).reduce(0, Integer::sum); 計算元素總和的方法其中暗含了裝箱成本莫湘,map(Person::getAge) 方法過后流變成了 Stream<Integer> 類型偏友,而每個 Integer 都要拆箱成一個原始類型再進行 sum 方法求和患膛,這樣大大影響了效率。

針對這個問題 Java 8 有良心地引入了數(shù)值流 IntStream, DoubleStream, LongStream,這種流中的元素都是原始數(shù)據(jù)類型涝滴,分別是 int,double胶台,long

1. 流與數(shù)值流的轉(zhuǎn)換

流轉(zhuǎn)換為數(shù)值流

  • mapToInt(T -> int) : return IntStream
  • mapToDouble(T -> double) : return DoubleStream
  • mapToLong(T -> long) : return LongStream
        //查詢表中serial_number=6的數(shù)據(jù)
        List<ATag> list = aTagMapper.selectList(new QueryWrapper<ATag>().eq("serial_number",6));
        LongStream longStream = list.stream().mapToLong(ATag::getId);

數(shù)值流轉(zhuǎn)換為流 boxed() 裝箱

Stream<Long> boxed = list.stream().mapToLong(ATag::getId).boxed();

流轉(zhuǎn)換為集合

List<Long> collect = list.stream().mapToLong(ATag::getId).boxed().collect(toList());
  • mapping()
    將集合中的某個字段拼接成一個新的數(shù)組
List<String> newList = list.stream().collect(mapping(ATag::getNameZh, toList()));

也可以寫成

List<String> newList2 = list.stream().map(ATag::getNameZh).collect(toList());

2. 數(shù)值流方法

  • sum()
  • max()
  • min()
  • average() 等...
int sum = list.stream().mapToInt(x -> x.intValue()).sum();
int min = list.stream().mapToInt(ATag::getSerialNumber).min().getAsInt();  

3. 數(shù)值范圍

IntStream 與 LongStream 擁有 range 和 rangeClosed 方法用于數(shù)值范圍處理

  • IntStream : rangeClosed(int, int) / range(int, int)
  • LongStream : rangeClosed(long, long) / range(long, long)
    這兩個方法的區(qū)別在于一個是閉區(qū)間歼疮,一個是半開半閉區(qū)間:
  • rangeClosed(1, 100) :[1, 100]
  • range(1, 100) :[1, 100)
    我們可以利用 IntStream.rangeClosed(1, 100) 生成 1 到 100 的數(shù)值流
        IntStream range = IntStream.rangeClosed(1, 100);
        int sum = range.sum();

二. Optional 類

NullPointerException 可以說是每一個 Java 程序員都非常討厭看到的一個詞,針對這個問題概作, Java 8 引入了一個新的容器類 Optional腋妙,可以代表一個值存在或不存在,這樣就不用返回容易出問題的 null讯榕。之前文章的代碼中就經(jīng)常出現(xiàn)這個類骤素,也是針對這個問題進行的改進。

Optional 類比較常用的幾個方法有:

isPresent() :值存在時返回 true愚屁,反之 flase
get() :返回當(dāng)前值济竹,若值不存在會拋出異常
orElse(T) :值存在時返回該值,否則返回 T 的值
Optional 類還有三個特化版本 OptionalInt霎槐,OptionalLong送浊,OptionalDouble,剛剛講到的數(shù)值流中的 max 方法返回的類型便是這個Optional 類其中其實還有很多學(xué)問,講解它說不定也要開一篇文章,這里先講那么多丘跌,先知道基本怎么用就可以袭景。

三. 構(gòu)建流

之前我們得到一個流是通過一個原始數(shù)據(jù)源轉(zhuǎn)換而來,其實我們還可以直接構(gòu)建得到流闭树。

1. 值創(chuàng)建流

Stream.of(T...) : Stream.of("aa", "bb") 生成流

        Stream<Integer> integerStream = Stream.of(1, 2, 3, 4);
  • Stream.empty() : 生成空流

2. 數(shù)組與流轉(zhuǎn)換

參考文章:int []數(shù)組與List互相轉(zhuǎn)換
根據(jù)參數(shù)的數(shù)組類型創(chuàng)建對應(yīng)的流:

  • Arrays.stream(T[ ])
  • Arrays.stream(int[ ])
  • Arrays.stream(double[ ])
  • Arrays.stream(long[ ])
數(shù)組轉(zhuǎn)換為流:
        int[] ints = {1, 2, 3, 4};
        IntStream stream = Arrays.stream(ints);

然后運用上面學(xué)到的裝箱與轉(zhuǎn)換集合耸棒,靈活地進行數(shù)組 --> 數(shù)值流 -->數(shù)組 --> 集合之間轉(zhuǎn)換
這里如果int[] 為 Integer[] 則數(shù)組轉(zhuǎn)換為集合不需要boxed()裝箱操作

        int[] ints = {1, 2, 3, 4};
        IntStream integerStream = Arrays.stream(ints);
        Integer[] integers = integerStream.boxed().toArray(Integer[]::new);
        List<Integer> list = Arrays.stream(ints).boxed().collect(toList());

輸出:

值得注意的是,還可以規(guī)定索引只取數(shù)組的某部分报辱,用到的是Arrays.stream(T[], int, int)

輸出:

3. 文件生成流

      Stream<String> stream = Files.lines(Paths.get("data.txt"));

每個元素是給定文件的其中一行
這個方法暫時不知道怎么用与殃,文件的路徑以及位置是怎樣的有大佬懂的請留言指教一下

4. 函數(shù)生成流

兩個操作可以創(chuàng)建所謂的無限流:不像從固定集合創(chuàng)建的流那樣有固定大小的流,配合limit()使用

  • iterate : 依次對每個新生成的值應(yīng)用函數(shù)
  • generate :接受一個函數(shù),生成一個新的值
      Stream.iterate(0, n -> n + 2).limit(10).collect(toList())
      Stream.generate(Math::random).limit(10).collect(toList())

輸出結(jié)果:

四. collect 收集數(shù)據(jù)

coollect 方法作為終端操作碍现,接受的是一個 Collector 接口參數(shù)幅疼,能對數(shù)據(jù)進行一些收集歸總操作
最常用的方法,把流中所有元素收集到一個 List, Set 或 Collection 中(個人理解就是將流轉(zhuǎn)換為集合)

  • toList
  • toSet
  • toCollection
  • toMap
   List newlist = list.stream.collect(toList());
//如果 Map 的 Key 重復(fù)了昼接,可是會報錯的哦
Map<Integer, Person> map = list.stream().collect(toMap(Person::getAge, p -> p));

2. 匯總

(1)counting
用于計算總和:

int sum = list.stream().collect(summingInt(Person::getAge));

也可以簡化為:

int sum = list.stream().mapToInt(Person::getAge).sum();

也可以寫成

int sum = list.stream().map(Person::getAge).reduce(Interger::sum).get();

推薦使用第二種

(3)averagingInt爽篷,averagingLong,averagingDouble

用于求取平均值

Double average = list.stream().collect(averagingInt(Person::getAge));

當(dāng)然也可以這樣寫

OptionalDouble average = list.stream().mapToInt(Person::getAge).average();

不過要注意的是慢睡,這兩種返回的值是不同類型的

(4)summarizingInt狼忱,summarizingLong膨疏,summarizingDouble

這三個方法比較特殊,比如 summarizingInt 會返回 IntSummaryStatistics 類型

      IntSummaryStatistics intSummaryStatistics = list.stream().collect(summarizingInt(Integer::intValue));
      IntSummaryStatistics intSummaryStatistics = list.stream().collect(summarizingInt(ATag::getSerialNumber));

然后使用這個IntSummaryStatistics獲取所需要的值钻弄,比如統(tǒng)計個數(shù)佃却,均值,求和窘俺,最值饲帅,等...

3. 取最值

maxBy,minBy 兩個方法瘤泪,需要一個 Comparator 接口作為參數(shù)

      Optional<Person> optional = list.stream().collect(maxBy(comparing(Person::getAge)));

我們也可以直接使用 max 方法獲得同樣的結(jié)果

      Optional<Person> optional = list.stream().max(comparing(Person::getAge));

4.1 joining 連接字符串

也是一個比較常用的方法灶泵,對流里面的字符串元素進行連接,其底層實現(xiàn)用的是專門用于字符串連接的 StringBuilder

List<String> list = ImmutableList.of("Tom","Jack","Speike");
list.stream().collect(joining(","));
結(jié)果:Tom,Jack,Speike
list.stream().collect(joining(" and ", "Today ", " play games."));
結(jié)果:Today Tom and Jack and Speike play games.

tips:

  • 當(dāng)我們使用StringJoiner(CharSequence delimiter)初始化一個StringJoiner的時候对途,這個delimiter其實是分隔符赦邻,并不是可變字符串的初始值。
  • StringJoiner(CharSequence delimiter,CharSequence prefix,CharSequence suffix)的第二個和第三個參數(shù)分別是拼接后的字符串的前綴和后綴实檀。
    參考文章:Java——StringJoiner

5. groupingBy 分組

groupingBy 用于將數(shù)據(jù)分組惶洲,最終返回一個 Map 類型

      Map<Integer, List<Person>> map = list.stream().collect(groupingBy(Person::getAge));

例子中我們按照年齡 age 分組,每一個 Person 對象中年齡相同的歸為一組
另外可以看出膳犹,Person::getAge 決定 Map 的鍵(Integer 類型)恬吕,list 類型決定 Map 的值(List<Person> 類型)

多級分組

groupingBy 可以接受一個第二參數(shù)實現(xiàn)多級分組:

Map<Integer, Map<Date, List<ATag>>> map = list.stream().collect(groupingBy(ATag::getSerialNumber, groupingBy(ATag::getCreateTime)));

例如上面代碼先按序號分組,再按創(chuàng)建時間分組须床,當(dāng)然在時間分組后面再添加分組groupingBy(...)
其中返回的 Map 鍵為 Integer 類型铐料,值為 Map<T, List<Person>> 類型,即參數(shù)中 groupBy(...) 返回的類型
tips:
返回的Map為無序的集合豺旬,根據(jù)業(yè)務(wù)需求可以將Map轉(zhuǎn)換為有序的LinkedHashMap

LinkedHashMap<Integer, List<ATag>> map = list.stream().collect(groupingBy(ATag::getSerialNumber, LinkedHashMap::new, toList()));
按組收集數(shù)據(jù)
Map<Integer, Integer> map = list.stream().collect(groupingBy(Person::getAge, summingInt(Person::getAge)));

該例子中钠惩,我們通過年齡進行分組,然后 summingInt(Person::getAge)) 分別計算每一組的年齡總和(Integer)族阅,最終返回一個 Map<Integer, Integer>
根據(jù)這個方法篓跛,我們可以知道,前面我們寫的:

groupingBy(Person::getAge)

其實等同于:

groupingBy(Person::getAge, toList())

6. partitioningBy 分區(qū)

分區(qū)與分組的區(qū)別在于耘分,分區(qū)是按照 true 和 false 來分的举塔,因此partitioningBy 接受的參數(shù)的 lambda 也是 T -> boolean

Map<Boolean, List<Person>> map = list.stream().collect(partitioningBy(p -> p.getAge() <= 20));

結(jié)果:
打印輸出

{
    false=[Person{name='mike', age=25}, Person{name='tom', age=30}], 
    true=[Person{name='jack', age=20}]
}

同樣地 partitioningBy 也可以添加一個收集器作為第二參數(shù)绑警,進行類似 groupBy 的多重分區(qū)等等操作求泰。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市计盒,隨后出現(xiàn)的幾起案子渴频,更是在濱河造成了極大的恐慌,老刑警劉巖北启,帶你破解...
    沈念sama閱讀 216,843評論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件卜朗,死亡現(xiàn)場離奇詭異拔第,居然都是意外死亡,警方通過查閱死者的電腦和手機场钉,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,538評論 3 392
  • 文/潘曉璐 我一進店門蚊俺,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人逛万,你說我怎么就攤上這事泳猬。” “怎么了宇植?”我有些...
    開封第一講書人閱讀 163,187評論 0 353
  • 文/不壞的土叔 我叫張陵得封,是天一觀的道長。 經(jīng)常有香客問我指郁,道長忙上,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,264評論 1 292
  • 正文 為了忘掉前任闲坎,我火速辦了婚禮疫粥,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘箫柳。我一直安慰自己手形,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,289評論 6 390
  • 文/花漫 我一把揭開白布悯恍。 她就那樣靜靜地躺著库糠,像睡著了一般。 火紅的嫁衣襯著肌膚如雪涮毫。 梳的紋絲不亂的頭發(fā)上瞬欧,一...
    開封第一講書人閱讀 51,231評論 1 299
  • 那天,我揣著相機與錄音罢防,去河邊找鬼艘虎。 笑死,一個胖子當(dāng)著我的面吹牛咒吐,可吹牛的內(nèi)容都是我干的野建。 我是一名探鬼主播,決...
    沈念sama閱讀 40,116評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼恬叹,長吁一口氣:“原來是場噩夢啊……” “哼候生!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起绽昼,我...
    開封第一講書人閱讀 38,945評論 0 275
  • 序言:老撾萬榮一對情侶失蹤唯鸭,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后硅确,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體目溉,經(jīng)...
    沈念sama閱讀 45,367評論 1 313
  • 正文 獨居荒郊野嶺守林人離奇死亡明肮,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,581評論 2 333
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了缭付。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片柿估。...
    茶點故事閱讀 39,754評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖陷猫,靈堂內(nèi)的尸體忽然破棺而出官份,到底是詐尸還是另有隱情,我是刑警寧澤烙丛,帶...
    沈念sama閱讀 35,458評論 5 344
  • 正文 年R本政府宣布舅巷,位于F島的核電站,受9級特大地震影響河咽,放射性物質(zhì)發(fā)生泄漏钠右。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,068評論 3 327
  • 文/蒙蒙 一忘蟹、第九天 我趴在偏房一處隱蔽的房頂上張望飒房。 院中可真熱鬧,春花似錦媚值、人聲如沸狠毯。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,692評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽嚼松。三九已至,卻和暖如春锰扶,著一層夾襖步出監(jiān)牢的瞬間献酗,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,842評論 1 269
  • 我被黑心中介騙來泰國打工坷牛, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留罕偎,地道東北人。 一個月前我還...
    沈念sama閱讀 47,797評論 2 369
  • 正文 我出身青樓京闰,卻偏偏與公主長得像颜及,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子蹂楣,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,654評論 2 354

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

  • 上一篇文章我講解 Stream 流的基本原理俏站,以及它的基本方法使用,本篇文章我們繼續(xù)講解流的其他操作沒有看過上篇文...
    Howie_Y閱讀 46,936評論 2 63
  • 原文地址: 深藍至尊 一. 流式處理簡介 在我接觸到j(luò)ava8流式處理的時候爱葵,我的第一感覺是流式處理讓集合操作變得...
    咻咻咻i閱讀 1,147評論 0 0
  • Java8 in action 沒有共享的可變數(shù)據(jù)施戴,將方法和函數(shù)即代碼傳遞給其他方法的能力就是我們平常所說的函數(shù)式...
    鐵牛很鐵閱讀 1,229評論 1 2
  • 今天和好友打了一盤籃球游戲反浓,原本兩方相持不下,因為我不小心用一個三分球能力低下的球員赞哗,投入了一個關(guān)鍵三分球雷则,他心態(tài)...
    蘇格拉磊閱讀 190評論 0 0
  • 前情回顧:薛付水(點擊閱讀) 回到柳城之后不久,蕭安踏上重返蒼鳴山的路程肪笋,不料卻在途中遭遇埋伏月劈,落入藏天閣手中,遭...
    古風(fēng)沐沐閱讀 858評論 0 0