巧用Java8中的Stream,讓集合操作6到飛起!!!

2021-03-02 腳本之家

本文經由掘金作者 堅持就是勝利 授權轉載

原文連結:https://juejin.im/post/5d5e2616f265da03b638b28a

如若轉載請聯繫原作者

簡介

java8也出來好久了,接口默認方法,lambda表達式,函數式接口,Date API等特性還是有必要去了解一下。比如在項目中經常用到集合,遍歷集合可以試下lambda表達式,經常還要對集合進行過濾和排序,Stream就派上用場了。用習慣了,不得不說真的很好用。

Stream作為java8的新特性,基於lambda表達式,是對集合對象功能的增強,它專注於對集合對象進行各種高效、便利的聚合操作或者大批量的數據操作,提高了編程效率和代碼可讀性。

Stream的原理:將要處理的元素看做一種流,流在管道中傳輸,並且可以在管道的節點上處理,包括過濾篩選、去重、排序、聚合等。元素流在管道中經過中間操作的處理,最後由最終操作得到前面處理的結果。

集合有兩種方式生成流:

上圖中是Stream類的類結構圖,裡面包含了大部分的中間和終止操作。

中間操作主要有以下方法(此類型方法返回的都是Stream):map (mapToInt, flatMap 等)、 filter、 distinct、 sorted、 peek、 limit、 skip、 parallel、 sequential、 unordered

終止操作主要有以下方法:forEach、 forEachOrdered、 toArray、 reduce、 collect、 min、 max、 count、 anyMatch、 allMatch、 noneMatch、 findFirst、 findAny、 iterator

舉例說明

首先為了說明Stream對對象集合的操作,新建一個Student類(學生類),覆寫了equals()和hashCode()方法

public class Student {

    private Long id;

    private String name;

    private int age;

    private String address;

    public Student() {}

    public Student(Long id, String name, int age, String address) {
        this.id = id;
        this.name = name;
        this.age = age;
        this.address = address;
    }

    @Override
    public String toString() {
        return "Student{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", age=" + age +
                ", address='" + address + '\'' +
                '}';
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Student student = (Student) o;
        return age == student.age &&
                Objects.equals(id, student.id) &&
                Objects.equals(name, student.name) &&
                Objects.equals(address, student.address);
    }

    @Override
    public int hashCode() {
        return Objects.hash(id, name, age, address);
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

}

filter(篩選)

public static void main(String [] args) {

        Student s1 = new Student(1L, "肖戰", 15, "浙江");
        Student s2 = new Student(2L, "王一博", 15, "湖北");
        Student s3 = new Student(3L, "楊紫", 17, "北京");
        Student s4 = new Student(4L, "李現", 17, "浙江");
        List<Student> students = new ArrayList<>();
        students.add(s1);
        students.add(s2);
        students.add(s3);
        students.add(s4);

        List<Student> streamStudents = testFilter(students);
        streamStudents.forEach(System.out::println);
    }
    /**
     * 集合的篩選
     * @param students
     * @return
     */
    private static List<Student> testFilter(List<Student> students) {
        //篩選年齡大於15歲的學生
//        return students.stream().filter(s -> s.getAge()>15).collect(Collectors.toList());
        //篩選住在浙江省的學生
        return students.stream().filter(s ->"浙江".equals(s.getAddress())).collect(Collectors.toList());
    }

運行結果:

這裡我們創建了四個學生,經過filter的篩選,篩選出地址是浙江的學生集合。

map(轉換)

    public static void main(String [] args) {

        Student s1 = new Student(1L, "肖戰", 15, "浙江");
        Student s2 = new Student(2L, "王一博", 15, "湖北");
        Student s3 = new Student(3L, "楊紫", 17, "北京");
        Student s4 = new Student(4L, "李現", 17, "浙江");
        List<Student> students = new ArrayList<>();
        students.add(s1);
        students.add(s2);
        students.add(s3);
        students.add(s4);

        testMap(students);
    }

    /**
     * 集合轉換
     * @param students
     * @return
     */
    private static void testMap(List<Student> students) {
        //在地址前面加上部分信息,只獲取地址輸出
        List<String> addresses = students.stream().map(s ->"住址:"+s.getAddress()).collect(Collectors.toList());
        addresses.forEach(a ->System.out.println(a));
    }

運行結果

map就是將對應的元素按照給定的方法進行轉換。

distinct(去重)

    public static void main(String [] args) {

      testDistinct1();
    }

    /**
     * 集合去重(基本類型)
     */
    private static void testDistinct1() {
        //簡單字符串的去重
        List<String> list = Arrays.asList("111","222","333","111","222");
        list.stream().distinct().forEach(System.out::println);
    }

運行結果:

public static void main(String [] args) {

      testDistinct2();
    }

    /**
     * 集合去重(引用對象)
     */
    private static void testDistinct2() {
        //引用對象的去重,引用對象要實現hashCode和equal方法,否則去重無效
        Student s1 = new Student(1L, "肖戰", 15, "浙江");
        Student s2 = new Student(2L, "王一博", 15, "湖北");
        Student s3 = new Student(3L, "楊紫", 17, "北京");
        Student s4 = new Student(4L, "李現", 17, "浙江");
        Student s5 = new Student(1L, "肖戰", 15, "浙江");
        List<Student> students = new ArrayList<>();
        students.add(s1);
        students.add(s2);
        students.add(s3);
        students.add(s4);
        students.add(s5);
        students.stream().distinct().forEach(System.out::println);
    }

運行結果:

可以看出,兩個重複的「肖戰」同學進行了去重,這不僅因為使用了distinct()方法,而且因為Student對象重寫了equals和hashCode()方法,否則去重是無效的。

sorted(排序)

    public static void main(String [] args) {

        testSort1();
    }

    /**
     * 集合排序(默認排序)
     */
    private static void testSort1() {
        List<String> list = Arrays.asList("333","222","111");
        list.stream().sorted().forEach(System.out::println);
    }

運行結果:

    public static void main(String [] args) {

        testSort2();
    }

    /**
     * 集合排序(指定排序規則)
     */
    private static void testSort2() {
        Student s1 = new Student(1L, "肖戰", 15, "浙江");
        Student s2 = new Student(2L, "王一博", 15, "湖北");
        Student s3 = new Student(3L, "楊紫", 17, "北京");
        Student s4 = new Student(4L, "李現", 17, "浙江");
        List<Student> students = new ArrayList<>();
        students.add(s1);
        students.add(s2);
        students.add(s3);
        students.add(s4);
        students.stream()
                .sorted((stu1,stu2) ->Long.compare(stu2.getId(), stu1.getId()))
                .sorted((stu1,stu2) -> Integer.compare(stu2.getAge(),stu1.getAge()))
                .forEach(System.out::println);
    }

運行結果:

上面指定排序規則,先按照學生的id進行降序排序,再按照年齡進行降序排序

limit(限制返回個數)

    public static void main(String [] args) {

        testLimit();
    }

    /**
     * 集合limit,返回前幾個元素
     */
    private static void testLimit() {
        List<String> list = Arrays.asList("333","222","111");
        list.stream().limit(2).forEach(System.out::println);
    }

運行結果:

skip(刪除元素)

    public static void main(String [] args) {

        testSkip();
    }

    /**
     * 集合skip,刪除前n個元素
     */
    private static void testSkip() {
        List<String> list = Arrays.asList("333","222","111");
        list.stream().skip(2).forEach(System.out::println);
    }

運行結果:

reduce(聚合)

    public static void main(String [] args) {
        testReduce();
    }
    /**
     * 集合reduce,將集合中每個元素聚合成一條數據
     */
    private static void testReduce() {
        List<String> list = Arrays.asList("歡","迎","你");
        String appendStr = list.stream().reduce("北京",(a,b) -> a+b);
        System.out.println(appendStr);
    }

運行結果:

min(求最小值)

    public static void main(String [] args) {
        testMin();
    }

    /**
     * 求集合中元素的最小值
     */
    private static void testMin() {
        Student s1 = new Student(1L, "肖戰", 14, "浙江");
        Student s2 = new Student(2L, "王一博", 15, "湖北");
        Student s3 = new Student(3L, "楊紫", 17, "北京");
        Student s4 = new Student(4L, "李現", 17, "浙江");
        List<Student> students = new ArrayList<>();
        students.add(s1);
        students.add(s2);
        students.add(s3);
        students.add(s4);
        Student minS = students.stream().min((stu1,stu2) ->Integer.compare(stu1.getAge(),stu2.getAge())).get();
        System.out.println(minS.toString());
    }

運行結果:

上面是求所有學生中年齡最小的一個,max同理,求最大值。

anyMatch/allMatch/noneMatch(匹配)

    public static void main(String [] args) {
        testMatch();
    }

    private static void testMatch() {
        Student s1 = new Student(1L, "肖戰", 15, "浙江");
        Student s2 = new Student(2L, "王一博", 15, "湖北");
        Student s3 = new Student(3L, "楊紫", 17, "北京");
        Student s4 = new Student(4L, "李現", 17, "浙江");
        List<Student> students = new ArrayList<>();
        students.add(s1);
        students.add(s2);
        students.add(s3);
        students.add(s4);
        Boolean anyMatch = students.stream().anyMatch(s ->"湖北".equals(s.getAddress()));
        if (anyMatch) {
            System.out.println("有湖北人");
        }
        Boolean allMatch = students.stream().allMatch(s -> s.getAge()>=15);
        if (allMatch) {
            System.out.println("所有學生都滿15周歲");
        }
        Boolean noneMatch = students.stream().noneMatch(s -> "楊洋".equals(s.getName()));
        if (noneMatch) {
            System.out.println("沒有叫楊洋的同學");
        }
    }

運行結果:

anyMatch:Stream 中任意一個元素符合傳入的 predicate,返回 true

allMatch:Stream 中全部元素符合傳入的 predicate,返回 true

noneMatch:Stream 中沒有一個元素符合傳入的 predicate,返回 true

總結

上面介紹了Stream常用的一些方法,雖然對集合的遍歷和操作可以用以前常規的方式,但是當業務邏輯複雜的時候,你會發現代碼量很多,可讀性很差,明明一行代碼解決的事情,你卻寫了好幾行。試試lambda表達式,試試Stream,你會有不一樣的體驗。

更多精彩

在公眾號後臺對話框輸入以下關鍵詞

查看更多優質內容!

女朋友 | 大數據 | 運維 | 書單 | 算法

大數據 | JavaScript | Python | 黑客

AI | 人工智慧 | 5G | 區塊鏈

機器學習 | 數學 | 送書

相關焦點

  • 巧用Java8中的Stream,讓集合操作飛起來!
    比如在項目中經常用到集合,遍歷集合可以試下lambda表達式,經常還要對集合進行過濾和排序,Stream就派上用場了。用習慣了,不得不說真的很好用。Stream作為java8的新特性,基於lambda表達式,是對集合對象功能的增強,它專注於對集合對象進行各種高效、便利的聚合操作或者大批量的數據操作,提高了編程效率和代碼可讀性。
  • 【Java】Java8之流Stream
    1 匹配聚合操作 allMatch:接收一個 Predicate 函數,當流中每個元素都符合該斷言時才返回true,否則返回false noneMatch:接收一個 Predicate 函數,當流中每個元素都不符合該斷言時才返回true,否則返回false anyMatch:接收一個 Predicate 函數,只要流中有一個元素滿足該斷言則返回
  • java集合【6】——— Iterable接口
    下面我們分別展示三個接口的調用:1.1  iterator()方法iterator()方法,是接口中的核心方法,主要是獲取迭代器,獲取到的iterator有next(),hasNext(),remove()等方法。
  • 程序|只要使用這個功能,程序運行速度瞬間提升,高到離譜!
    我是小小,今天是本周的第二篇,本篇將會著重的講解關於Java並行流的知識前言在之前如果需要處理集合需要先手動分成幾部分,然後為每部分創建線程,最後在合適的時候合併,這是手動處理並行集合的方法,在java8中,有了新功能,可以一下開啟並行模式。
  • How To Live Stream On WeChat
    Within 20 mins our fake toilet live stream had 42 viewers. You dirty people!To make your own live stream long press the QR code below to download the Live Pepper APP. (Alternatively go to their website: http://www.huajiao.com/ )
  • 換個人的話,這種操作會被嘲到飛起吧……?
    7天上了6個熱搜,不得不說鄧超真的太努力了,努力到讓人害怕……掐指一算,僅用2個話題就能玩出6個熱搜其實,宣傳期頻繁露臉曝光是正常操作,畢竟主創都有為作品宣傳奔走相告的義務嘛,而【上熱搜】就是最直接最省事的方法。
  • 快速夾緊臺虎鉗的巧改巧用
    一、臺虎鉗巧改巧用下面通過對臺虎鉗進行了適當改造,形成了不同功能的臺虎鉗,從而進一步提高了它的應用範圍,生產效率和加工精度,同時也減輕了工人工作的強度,可謂是人性化的改造與創新。1.1特種臺虎鉗的巧改巧用圖1是在臺虎鉗上表面加工平整後,在兩個鉗口上各鑽兩個平行的孔,插入有凸臺的滑配銷子,將工件緊固在四個銷子之間。因為當工件太大時,不能用一般臺虎鉗夾緊,往往需要用很多時間將其固定在床臺上,而圖1用銷子可以穩妥地將其壓緊。
  • Out-Stream Video ADs,視頻網站之外的天地任你闖咯
    -- 近些年,廣告主投放數字視頻廣告的熱情越來越高,高到那些優質的視頻廣告位簡直已經不夠用。大劇前貼片廣告長到70秒,剛剛興起的OTT端前貼片也已經到了30秒,這在兩年前你可敢信?! 社交、新聞,甚至搜索站點中擁有大量誘人的上下文內容,視頻廣告若能穿插其中或許還會獲得更好的投放效果不是?
  • 無序的集合:Python中的數學集合運算
    圖源:unsplashset是Python中無序的集合,它可以用於計算標準數學運算,例如交集、併集、差集和對稱差集,Other集合(例如列表、元組和字典)不支持集合操作,Dict視圖對象類似於集合,可以進行集合操作。
  • 巧用2個操作,就能瞬間找到!
    為了讓小夥伴們更好地了解遊戲中的內容,希望大家能動動小手關注一下。本期內容去往末地擊殺了末影龍後,在周圍可以找到折躍門可以進入外島。之所以要去往末地外島,是因為這裡有著末地城的存在。很多非酋玩家剛進入外島時,通過徒步很難找到末地城的蹤跡,這時候巧用2個操作,就能瞬間找到。
  • 全新外星人m15/m17超輕悍筆記本 帶你刷新遊戲感官體驗6到飛起!
    近日,343工作室宣布,《光環:士官長合集》中的《光環3》的PC BETA公測將於6月正式開始。《光環3》被廣泛認為是Xbox玩家都不應該錯過的佳作,至今銷量早已破千萬。
  • 用電飯煲做出來的紅燒雞腿,好吃到飛起,做法也很簡單
    用雞肉能做出怎樣的美食?用電飯煲做出來的紅燒雞腿,好吃到飛起,需要準備的材料、雞腿6隻、蔥一把、薑片10片、老抽兩三勺、醬油兩勺、適量鹽、一點點油、一點點糖。(如果怕燒乾中途加一次水,到雞腿的一半,20分鐘翻一次身,不想翻身就加水沒過雞腿。儘量少一點水味道比較足。)
  • Karsa再現窺屏操作難掩青銅操作,Uzi變王境澤說不浪自己浪的飛起
    Karsa再現窺屏操作但是卻有兩個青銅操作,Uzi變身王境澤說大家不要不浪自己浪的飛起。在對陣RW的第一局中,RW上來就開始針對RNG打野香鍋,ban了三個打野,香鍋:唉,開始了啊。小虎:完了,香鍋表示:這是不讓我莽啊,比賽開始後,香鍋也是表示自己不莽了,這把打防守反擊就可以了。
  • 《歌手》植入玩得6到飛起,老司機QQ音樂你帶帶我~
    除了口播、數據露出,QQ音樂還多次融合節目場景,在明星的真實行為中出現。在第六期《歌手》錄製現場過生日的華晨宇,便收到來自QQ音樂特別定製的「終身豪華綠鑽」作為生日禮物,他與音樂合伙人更興奮表示「需要一個好耳機來欣賞QQ音樂的高音質」。
  • 用NVIDIA DALI 加速PyTorch:訓練速度提升 4 倍
    英偉達的最新產品,Tesla V100 & Geforce RTX 系列,包含特定的張量核,以加速常用的神經網絡操作。特別是,V100 已經具備足夠的性能。能夠以每秒數千幅圖像的速度訓練神經網絡。這使得在 ImageNet 數據集上的單一 GPU 訓練時間減少到幾個小時。而在 202 年,在 ImageNet 上訓練 AlexNet 模型花了 5 天時間!
  • 「綜藝大集合」錄製中,巧遇知名韓國網紅,即刻加入錄製
    中國臺灣省綜藝節目《綜藝大集合》,主要的拍攝內容,以主持人和嘉賓在中國臺灣省的各地大街小巷與民眾進行遊戲並發送獎金為主,主持人則是當年「四王一後」之一的胡瓜,以及阿翔。在街頭錄製,自然少不了一些有趣的偶遇。
  • 實戰 | 巧用亞萬+日航,朱eason的頭等艙環球之旅
    前幾天,他發了嬉遊君一篇投稿文章,巧用亞洲萬裡通和日航JAL的裡程計劃,實現自己的頭等艙+商務艙的環球計劃,看了嬉遊君自己也是心痒痒...以下的文,來自朱eason。知識點1)如何進行航線規劃前段時間突發奇想,剛好也是日本航空裡程宣布貶值的關係,想去世界的另一端走走。於是朱eason開始安排起自己的環球航線來。
  • Flink 中極其重要的 Time 與 Window 詳細解析(深度好文,建議收藏)
    滾動窗口分配器將每個元素分配到一個指定窗口大小的窗口中,滾動窗口有一個固定的大小,並且不會出現重疊。例如:如果你指定了一個5分鐘大小的滾動窗口,窗口的創建如下圖所示:滑動窗口分配器將元素分配到固定長度的窗口中,與滾動窗口類似,窗口的大小由窗口大小參數來配置,另一個窗口滑動參數控制滑動窗口開始的頻率。因此,滑動窗口如果滑動參數小於窗口大小的話,窗口是可以重疊的,在這種情況下元素會被分配到多個窗口中。例如,你有10分鐘的窗口和5分鐘的滑動,那麼每個窗口中5分鐘的窗口裡包含著上個10分鐘產生的數據,如下圖所示:
  • 香到飛起的蔥油拌麵 東楚網
    食材明細  主料  掛麵1把、老抽1勺  輔料  香蔥5克  香到飛起的蔥油拌麵的做法步驟  1香蔥去掉蔥白,留下青綠色的部分,把蔥切段  2碗中放入20克生抽、10克老抽和8克白糖、5克鹽調和均勻備用  3炒鍋放油用小火炸蔥段  4把蔥段炸至黑黃色時關火  5炸好的蔥油加入準備好的調料攪拌均勻即可