Java之Stream流的使用總結

Java—Stream

什么是Stream塞颁?

Java8 中,Collection 新增了兩個流方法婶熬,分別是 Stream() 和 parallelStream()

Java8 中添加了一個新的接口類 Stream本涕,相當于高級版的 Iterator吻谋,它可以通過 Lambda 表達式對集合進行大批量數(shù)據(jù)操作,或 者各種非常便利骄噪、高效的聚合數(shù)據(jù)操作佛舱。

為什么要使用 Stream

在 Java8 之前原杂,我們通常是通過 for 循環(huán)或者 Iterator 迭代來重新排序合并數(shù)據(jù)印颤,又或者通過重新定義 Collections.sorts 的 Comparator 方法來實現(xiàn),這兩種方式對于大數(shù)據(jù)量系統(tǒng)來說穿肄,效率并不是很理想年局。Stream 的聚合操作與數(shù)據(jù)庫 SQL 的聚合操作 sorted、filter咸产、map 等類似某宪。我們在應用層就可以高效地實現(xiàn)類似數(shù)據(jù)庫 SQL 的 聚合操作了,而在數(shù)據(jù)操作方面锐朴,Stream 不僅可以通過串行的方式實現(xiàn)數(shù)據(jù)操作兴喂,還可以通過并行的方式處理大批量數(shù)據(jù),提高數(shù)據(jù) 的處理效率。

Stream 使用入門

        @Test
    public void test1(){
        List<String> names = Arrays.asList("張三","李四","王五","趙柳","張五六七","王少","趙四","張仁","李星");
        //需求:找出 姓張中名字最長的
        int maxLengthStartWithZ = names.parallelStream()
                .filter(name -> name.startsWith("張"))
                .mapToInt(String::length)
                .max()
                .getAsInt();
        System.out.println(names.get(maxLengthStartWithZ));
    }

Stream 操作分類

官方將 Stream 中的操作分為兩大類:終結操作(Terminal operations)和中間操作(Intermediate operations)衣迷。

中間操作會返回一個新的流畏鼓,一個流可以后面跟隨零個或多個中間操作。其目的主要是打開流壶谒,做出某種程度的數(shù)據(jù)映射/過濾云矫,

然后會返回一個新的流,交給下一個操作使用汗菜。這類操作都是惰性化的(lazy)让禀,就是說,僅僅調用到這類方法陨界,并沒有真正開始流的

遍歷巡揍。而是在終結操作開始的時候才真正開始執(zhí)行。

中間操作又可以分為無狀態(tài)(Stateless)與有狀態(tài)(Stateful)操作:

? 無狀態(tài)是指元素的處理不受之前元素的影響菌瘪;

? 有狀態(tài)是指該操作只有拿到所有元素之后才能繼續(xù)下去腮敌。

終結操作是指返回最終的結果。一個流只能有一個終結操作俏扩,當這個操作執(zhí)行后糜工,這個流就被使用“光”了,無法再被操作录淡。所以

這必定這個流的最后一個操作捌木。終結操作的執(zhí)行才會真正開始流的遍歷,并且會生成一個結果嫉戚。

終結操作又可以分為短路(Short-circuiting)與非短路(Unshort-circuiting)操作刨裆,

? 短路是指遇到某些符合條件的元素就可以得到最終結果,

? 非短路是指必須處理完所有元素才能得到最終結果彼水。操作分類詳情如下圖所示:

image.png

個別方法的測試

public class StuWithStream {
    public static void main(String[] args) {
        List<Student> studentList =Datainit();
        groupBy(studentList);
       // filter(studentList);
//        total(studentList);
//        MaxAndMin(studentList);

    }
    public static List<Student> Datainit(){
        List<Student> students = Arrays.asList(
                new Student("小明", 168, "男"),
                new Student("大明", 182, "男"),
                new Student("小白", 174, "男"),
                new Student("小黑", 186, "男"),
                new Student("小紅", 156, "女"),
                new Student("小黃", 158, "女"),
                new Student("小青", 165, "女"),
                new Student("小紫", 172, "女"));
        return students;
    }
    //Stream實現(xiàn)分組
    public static  void groupBy(List<Student> studentsList){
        Map<String, List<Student>> groupBy = studentsList
                .stream()
                .collect(Collectors.groupingBy(Student::getSex));
        System.out.println("分組后:"+groupBy);
    }

    //Stream實現(xiàn)過濾
    public static  void filter(List<Student> studentsList){
        List<Student> filter = studentsList
                .stream()
                .filter(student->student.getHeight()>180)
                .collect(Collectors.toList());
        System.out.println("過濾后:"+filter);
    }

    //Stream實現(xiàn)求和
    public static  void total(List<Student> studentsList){
        int totalHeight = studentsList
                .stream()
                .mapToInt(Student::getHeight)
                .sum();
        System.out.println(totalHeight);
    }
    //Stream找最大和最小
    public static  void MaxAndMin(List<Student> studentsList){
        int maxHeight = studentsList
                .stream()
                .mapToInt(Student::getHeight)
                .max()
                .getAsInt();
        System.out.println("max:"+maxHeight);
        int minHeight = studentsList
                .stream()
                .mapToInt(Student::getHeight)
                .min()
                .getAsInt();
        System.out.println("min:"+minHeight);
    }



}

因為 Stream 操作類型非常多崔拥,總結一下常用的

  • map():將流中的元素進行再次加工形成一個新流,流中的每一個元素映射為另外的元素凤覆。
  • filter(): 返回結果生成新的流中只包含滿足篩選條件的數(shù)據(jù)
  • limit():返回指定數(shù)量的元素的流链瓦。返回的是 Stream 里前面的 n 個元素。
  • skip():和 limit()相反盯桦,將前幾個元素跳過(取出)再返回一個流慈俯,如果流中的元素小于或者等于 n,就會返回一個空的流拥峦。
  • sorted():將流中的元素按照自然排序方式進行排序贴膘。
  • distinct():將流中的元素去重之后輸出。
  • peek():對流中每個元素執(zhí)行操作略号,并返回一個新的流刑峡,返回的流還是包含原來流中的元素洋闽。

Stream 并發(fā) Stream 源碼實現(xiàn)

并發(fā)的處理函數(shù)對比 parallelStream()方法這里的并行處理指的是,Stream 結合了 ForkJoin 框架突梦,對 Stream 處理進行了分片诫舅,Splititerator 中estimateSize 方法會估算出分片的數(shù)據(jù)量。

通過預估的數(shù)據(jù)量獲取最小處理單元的閾值宫患,如果當前分片大小大于最小處理單元的閾值刊懈,就繼續(xù)切分集合。每個分片將會生成一個

Sink 鏈表娃闲,當所有的分片操作完成后虚汛,F(xiàn)orkJoin 框架將會合并分片任何結果集。

Stream 的性能

常規(guī)數(shù)據(jù)迭代

? 100 的性能對比常規(guī)的迭代 > Stream 并行迭代> Stream 串行迭代

為什么這樣:

1皇帮、常規(guī)迭代代碼簡單卷哩,越簡單的代碼執(zhí)行效率越高。

2玲献、Stream 串行迭代殉疼,使用了復雜的設計梯浪,導致執(zhí)行速度偏低捌年。所以是性能最低的。

3挂洛、Stream 并行迭代 使用了 Fork-Join 線程池,所以效率比 Stream 串行迭代快礼预,但是對比常規(guī)迭代還是要慢(畢竟設計和代碼復雜)

大數(shù)據(jù)迭代

? 一億的數(shù)組性能對比(默認線程池)為什么這樣:

1、Stream 并行迭代 使用了 Fork-Join 線程池, 而線程池線程數(shù)為 cpu 的核心數(shù)(我的電腦為 12 核)虏劲,大數(shù)據(jù)場景下托酸,能夠利用多線

程機制,所以效率比 Stream 串行迭代快柒巫,同時多線程機制切換帶來的開銷相對來說還不算多励堡,所以對比常規(guī)迭代還是要快(雖然設計

和代碼復雜)

2、常規(guī)迭代代碼簡單堡掏,越簡單的代碼執(zhí)行效率越高应结。

3、Stream 串行迭代泉唁,使用了復雜的設計鹅龄,導致執(zhí)行速度偏低。所以是性能最低的亭畜。

? 一億的數(shù)組性能對比(線程池數(shù)量=2)為什么這樣:

Stream 并行迭代 使用了 Fork-Join 線程池,大數(shù)據(jù)場景下扮休,雖然利用多線程機制,但是線程池線程數(shù)為 2拴鸵,所以多個請求爭搶著執(zhí)行任

務玷坠,想象對請求來說任務是被交替執(zhí)行完成蜗搔,所以對比常規(guī)迭代還是要慢(雖然用到了多線程技術)

? 一億的數(shù)組性能對比(線程池數(shù)量=240)為什么這樣:

Stream 并行迭代使用了 Fork-Join 線程池, 而線程池線程數(shù)為 240,大數(shù)據(jù)場景下八堡,雖然利用多線程機制碍扔,但是線程太多,線程的上下

文切換成本過高秕重,所以導致了執(zhí)行效率反而沒有常規(guī)迭代快不同。

如何合理使用 Stream?

我們可以看到:在循環(huán)迭代次數(shù)較少的情況下溶耘,常規(guī)的迭代方式性能反而更好二拐;而在大數(shù)據(jù)循環(huán)迭代中, parallelStream(合理的線程池數(shù)上)有一定的優(yōu)勢凳兵。

但是由于所有使用并行流 parallelStream 的地方都是使用同一個 Fork-Join 線程池百新,而線程池線程數(shù)僅為 cpu 的核心數(shù)。

切記庐扫,如果對底層不太熟悉的話請不要亂用并行流 parallerStream(尤其是你的服務器核心數(shù)比較少的情況下)

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末饭望,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子形庭,更是在濱河造成了極大的恐慌铅辞,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,968評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件萨醒,死亡現(xiàn)場離奇詭異斟珊,居然都是意外死亡,警方通過查閱死者的電腦和手機富纸,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評論 2 382
  • 文/潘曉璐 我一進店門囤踩,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人晓褪,你說我怎么就攤上這事堵漱。” “怎么了涣仿?”我有些...
    開封第一講書人閱讀 153,220評論 0 344
  • 文/不壞的土叔 我叫張陵勤庐,是天一觀的道長。 經(jīng)常有香客問我变过,道長埃元,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,416評論 1 279
  • 正文 為了忘掉前任媚狰,我火速辦了婚禮岛杀,結果婚禮上,老公的妹妹穿的比我還像新娘崭孤。我一直安慰自己类嗤,他們只是感情好糊肠,可當我...
    茶點故事閱讀 64,425評論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著遗锣,像睡著了一般货裹。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上精偿,一...
    開封第一講書人閱讀 49,144評論 1 285
  • 那天弧圆,我揣著相機與錄音,去河邊找鬼笔咽。 笑死搔预,一個胖子當著我的面吹牛,可吹牛的內容都是我干的叶组。 我是一名探鬼主播拯田,決...
    沈念sama閱讀 38,432評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼甩十!你這毒婦竟也來了船庇?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 37,088評論 0 261
  • 序言:老撾萬榮一對情侶失蹤侣监,失蹤者是張志新(化名)和其女友劉穎鸭轮,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體达吞,經(jīng)...
    沈念sama閱讀 43,586評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡张弛,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,028評論 2 325
  • 正文 我和宋清朗相戀三年荒典,在試婚紗的時候發(fā)現(xiàn)自己被綠了酪劫。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,137評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡寺董,死狀恐怖覆糟,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情遮咖,我是刑警寧澤滩字,帶...
    沈念sama閱讀 33,783評論 4 324
  • 正文 年R本政府宣布,位于F島的核電站御吞,受9級特大地震影響麦箍,放射性物質發(fā)生泄漏。R本人自食惡果不足惜陶珠,卻給世界環(huán)境...
    茶點故事閱讀 39,343評論 3 307
  • 文/蒙蒙 一挟裂、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧揍诽,春花似錦诀蓉、人聲如沸栗竖。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽狐肢。三九已至,卻和暖如春沥曹,著一層夾襖步出監(jiān)牢的瞬間份名,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評論 1 262
  • 我被黑心中介騙來泰國打工妓美, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留同窘,地道東北人。 一個月前我還...
    沈念sama閱讀 45,595評論 2 355
  • 正文 我出身青樓部脚,卻偏偏與公主長得像想邦,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子委刘,可洞房花燭夜當晚...
    茶點故事閱讀 42,901評論 2 345

推薦閱讀更多精彩內容