Java學習日誌(十六): Stream流詳解,Stream流綜合案例

JavaEE學習日誌持續更新----> 必看!JavaEE學習路線(文章總彙)

Stream

Stream:就是一個流式思想,可以把集合或數組,轉化爲一個Stream流,使用Stream流中的方法,來操作集合或數組

引言

現在,我們有一個集合,它有以下元素

list.add("張無忌");
list.add("周芷若");
list.add("趙敏");
list.add("張強");
list.add("張三丰");

需求:過濾掉集合中以張開頭的元素,然後過濾掉字符串長度爲3的元素,最後再遍歷輸出每個元素

使用傳統的方式(循環遍歷),對集合中的元素進行過濾,並遍歷集合

public class Demo01 {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("張無忌");
        list.add("周芷若");
        list.add("趙敏");
        list.add("張強");
        list.add("張三丰");

        //對list集合中的元素進行過濾,只要以張開頭的元素,存儲到一個新的集合
        List<String> listZhang = new ArrayList<>();
        for (String name : list) {
            if(name.startsWith("張")){
                listZhang.add(name);
            }
        }
        //對listZhang集合中的元素進行過濾,只要名字長度爲3的元素,存儲到一個新的集合
        List<String> listThree = new ArrayList<>();
        for (String name : listZhang) {
            if(name.length()==3){
                listThree.add(name);
            }
        }
        //遍歷listThree
        for (String name : listThree) {
            System.out.println(name);
        }
    }
}

這段代碼中含有三個循環,每一個作用不同:

  1. 首先篩選所有姓張的人;
  2. 然後篩選名字有三個字的人;
  3. 後進行對結果進行打印輸出。

每當我們需要對集合中的元素進行操作的時候,總是需要進行循環、循環、再循環。這是理所當然的麼?不是。循環是做事情的方式,而不是目的。另一方面,使用線性循環就意味着只能遍歷一次。如果希望再次遍歷,只能再使用另一個循環從頭開始。

所以就有了Lambda的衍生物Stream

示例:Stream可以使代碼更加優雅

public class Demo02 {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("張無忌");
        list.add("周芷若");
        list.add("趙敏");
        list.add("張強");
        list.add("張三丰");



        //對list集合中的元素進行過濾,只要以張開頭的元素,存儲到一個新的集合
        //對listZhang集合中的元素進行過濾,只要名字長度爲3的元素,存儲到一個新的集合
        //遍歷listThree
        //把list轉化爲流
        /*list.stream()
                .filter(name->name.startsWith("張"))
                .filter(name->name.length()==3)
                .forEach(name->System.out.println(name));*/
        //方法引用
        list.stream()
                .filter(name->name.startsWith("張"))//過濾以張開頭的元素
                .filter(name->name.length()==3)//過濾長度爲3的元素
                .forEach(System.out::println);//遍歷

    }
}

流式模型

Stream流:需要先創建一條流式模型,根據模型操作數據
在這裏插入圖片描述
注意:

  1. 這裏的 filter 、 map 、 skip 都是在對函數模型進行操作,集合元素並沒有真正被處理。只有當終結方法 count 執行的時候,整個模型纔會按照指定策略執行操作。而這得益於Lambda的延遲執行特性。
  2. Stream流只操作數據,並不保存數據。當一次操作結束以後,會產生一個新的Stream流,原來的Stream流會關閉。

獲取Stream流的方式

java.util.stream.Stream<泛型> 是Java 8新加入的最常用的流接口

獲取Stream流的方式

  1. 所有的Collection集合都可以通過stream默認方法獲取流。
    default Stream<E> stream() :返回以此集合爲源的順序 Stream 。
    注意:此方法都是單列集合Collection中的方法,Map集合不能直接使用
  2. Stream接口的靜態方法of可以獲取數組對應的流
    static <T> Stream<T> of​(T... values) :傳遞可變參數,把可變參數轉換爲Stream流。
    注意:可變參數底層就是一個數組,所以也可以傳遞數組,創建Stream流

示例一:Collection集合獲取Stream流

  1. list集合獲取Stream流
//創建list集合
List<String> list = new ArrayList<>();
//使用List集合中的方法stream,把集合轉化爲流
Stream<String> stream1 = list.stream();
  1. set集合獲取Stream流
//創建set集合
Set<String> set = new HashSet<>();
//使用set集合中的方法stream,把集合轉化爲流
Stream<String> stream2 = set.stream();
  1. Map集合獲取Stream流(間接獲取)
//第一種:轉化爲set或Colleciton集合來獲取流
Map<String,String> map = new HashMap<>();
Set<String> keySet = map.keySet();//獲取所有的鍵
Stream<String> stream3 = keySet.stream();

Collection<String> values = map.values();//獲取所有的值
Stream<String> stream4 = values.stream();

//第二種:通過entry來獲取流
Set<Map.Entry<String, String>> entries = map.entrySet();
Stream<Map.Entry<String, String>> stream5 = entries.stream();

示例二:數組獲取Stream流

  1. 可變參數獲取Stream流
//Stream接口的靜態方法of創建stream流
Stream<String> stream1 = Stream.of("a", "b", "c", "d");

Stream<Integer> stream2 = Stream.of(1,2,3,4);
  1. 數組獲取流
//可變參數底層就是一個數組,所以也可以傳遞數組,創建Stream流

String[] arr1 = {"a", "b", "c", "d"};
Stream<String> stream3 = Stream.of(arr1);

Integer[] arr2 = {1,2,3,4};
Stream<Integer> stream4 = Stream.of(arr2);

注意:以下寫法錯誤,數組的數據類型必須使用包裝類不能使用基本類型

int[] arr3 = {1,2,3,4,5};
Stream<int[]> stream5 = Stream.of(arr3);

常用方法

Stream流的方法分爲兩種方法:

  • 終結方法:返回值類型不再是 Stream 接口自身類型的方法,因此不再支持類似 StringBuilder 那樣的鏈式調 用。本小節中,終結方法包括 count 和 forEach 方法
  • 非終結方法:返回值類型仍然是 Stream 接口自身類型的方法,因此支持鏈式調用。(除了終結方法外,其餘 方法均爲非終結方法。)

foreach:遍歷

void forEach​(Consumer<? super T> action) 對此流的每個元素執行操作。
是一個終結方法,返回值不是Stream,不能再使用Stream中的方法了。
參數:函數式接口Consumer,所以可以使用lambda表達式,重寫accept方法

作用:對Stream流進行遍歷

示例:

public class Demo02 {
    public static void main(String[] args) {
        //創建一個Stream流
        Stream<String> stream = Stream.of("美羊羊", "喜羊羊", "慢羊羊", "懶羊羊", "沸羊羊", "灰太狼", "紅太狼", "小灰灰");
       	//方法引用
        stream.forEach(System.out::println);
    }
}

注意:若使用了兩次Stream流,則會報IllegalStateException: stream has already been operated upon or closed異常。第二次使用Stream中的方法forEach,拋出了非法的狀態異常,流是一個流式模型,流使用完畢之後,數據就流轉到下一步,這時上一步的流就已經關閉,所以就不能再使用了,Stream流只能使用一次。

public class Demo02 {
    public static void main(String[] args) {
        //創建一個Stream流
        Stream<String> stream = Stream.of("美羊羊", "喜羊羊", "慢羊羊", "懶羊羊", "沸羊羊", "灰太狼", "紅太狼", "小灰灰");
        //第一次使用Stream流
        stream.forEach( s-> System.out.println(s));
        //第二次使用Stream流,出現異常!
        stream.forEach(System.out::println);
    }
}

filter:過濾

Stream<T> filter​(Predicate<? super T> predicate) 返回由與此給定謂詞匹配的此流的元素組成的流。
參數:函數式接口Predicate,所以可以使用lambda表達式,重寫test方法

作用:對Stream流中的元素進行過濾,形成一個新的Stream流

示例:對Stream流中的元素進行過濾,只要包含"羊羊",並對新Stream流進行遍歷

public class Demo03 {
    public static void main(String[] args) {
        //創建一個Stream流
        Stream<String> stream = Stream.of("美羊羊", "喜羊羊", "慢羊羊", "懶羊羊", "沸羊羊", "灰太狼", "紅太狼", "小灰灰");
        //對Stream流中的元素進行過濾,只要包含"羊羊"
        Stream<String> streamYY = stream.filter(s -> s.contains("羊羊"));
        //遍歷Stream
        streamYY.forEach(System.out::println);
    }
}

count:統計

long count() 返回此流中元素的數量。
是一個終結方法,返回值不是Stream,不能再使用Stream中的方法了

作用:統計Stream流中元素的個數

示例:

public class Demo04 {
    public static void main(String[] args) {
        //創建一個Stream流
        Stream<String> stream = Stream.of("美羊羊", "喜羊羊", "慢羊羊", "懶羊羊", "沸羊羊", "灰太狼", "紅太狼", "小灰灰");
        long count = stream.count();
        System.out.println(count);//8
    }
}

limit:獲取

Stream<T> limit​(long maxSize) 返回由此流的元素組成的流,截斷長度不超過 maxSize。

作用:獲取前幾個元素,把元素存儲到一個新的Stream流中

例如:

  • limit(3)獲取流中前3個元素
  • limit(n)獲取流中前n個元素

示例:獲取Stream流中前5個元素,並遍歷

public class Demo05 {
    public static void main(String[] args) {
        //創建一個Stream流
        Stream<String> stream = Stream.of("美羊羊", "喜羊羊", "慢羊羊", "懶羊羊", "沸羊羊", "灰太狼", "紅太狼", "小灰灰");

        //獲取Stream流中的前5個元素
        Stream<String> limitStream = stream.limit(5);
        //遍歷
        limitStream.forEach(System.out::println);
    }
}

skip:跳過

Stream<T> skip​(long n) 在丟棄流的第一個 n元素後,返回由此流的其餘元素組成的流。

作用:跳過前幾個元素,把剩餘的元素存儲到一個新的Stream流中

例如:skip(3):跳過前三個元素

示例:跳過Stream流的前5個元素,並遍歷

public class Demo06 {
    public static void main(String[] args) {
        //創建一個Stream流
        Stream<String> stream = Stream.of("美羊羊", "喜羊羊", "慢羊羊", "懶羊羊", "沸羊羊", "灰太狼", "紅太狼", "小灰灰");
        //跳過前五個元素
        Stream<String> stringStream = stream.skip(5);
        //遍歷
        stringStream.forEach(System.out::println);
    }
}

map:映射

<R> Stream<R> map​(Function<? super T,​? extends R> mapper) 返回一個流,該流包含將給定函數應用於此流的元素的結果。
參數:函數式接口Function,所以可以使用lambda表達式,重寫apply方法

作用:映射,把String流的數據類型轉化爲另外一種數據類型的Stream流

示例:使用map方法把String類型的Stream映射爲Integer類型的Stream

public class Demo07 {
    public static void main(String[] args) {
        //創建一個Stream流
        Stream<String> stringStream = Stream.of("1", "2", "3", "4", "5");
        //使用map方法把String類型的Stream映射爲Integer類型的Stream
        Stream<Integer> integerStream = stringStream.map((String s) -> {
            return Integer.parseInt(s);
        });
        //遍歷
        integerStream.forEach(System.out::println);

    }
}

練習:使用map方法把String類型的姓名映射爲Person類型的對象

public class Demo08 {
    public static void main(String[] args) {
        //創建一個存儲String的Stream流
        Stream<String> stream = Stream.of("張三", "李四", "王五", "趙六", "田七");
        //Stream<Person> personStream = stream.map(name-> new Person(name));
        Stream<Person> personStream = stream.map(Person::new);
        //遍歷
        personStream.forEach(System.out::println);
    }
}

concat(靜態方法):組合

static <T> Stream<T> concat​(Stream<? extends T> a, Stream<? extends T> b) 創建一個延遲連接的流,其元素是第一個流的所有元素,後跟第二個流的所有元素。

作用:把兩個Stream流合成一個

示例:使用Stream中的靜態方法concat,把兩個流合成一個新的流

public class Demo09 {
    public static void main(String[] args) {
        //創建一個Stream1流
        Stream<String> stream1 = Stream.of("張三", "李四", "王五", "趙六", "田七");
        //創建一個Stream2流
        Stream<String> stream2 = Stream.of("美羊羊", "喜羊羊", "慢羊羊", "懶羊羊", "沸羊羊", "灰太狼", "紅太狼", "小灰灰");
        //使用Stream中的靜態方法concat,把兩個流合成一個新的流
        Stream<String> stream = Stream.concat(stream1, stream2);
        //遍歷
        stream.forEach(System.out::println);
    }
}

收集Stream結果

Stream流轉化爲集合

  1. 轉化爲List集合:stream.collect(Collectors.toList());
public class Demo01 {
    public static void main(String[] args) {
        //創建一個Stream流
        Stream<String> stream = Stream.of("美羊羊", "喜羊羊", "慢羊羊", "懶羊羊", "沸羊羊", "灰太狼", "紅太狼", "小灰灰");
        //轉化爲List集合:stream.collect(Collectors.toList());
        List<String> list = stream.collect(Collectors.toList());
        System.out.println(list);

        }
}
  1. 轉化爲Set集合:stream.collect(Collectors.toSet());
public class Demo01 {
    public static void main(String[] args) {
        //創建一個Stream流
        Stream<String> stream = Stream.of("美羊羊", "喜羊羊", "慢羊羊", "懶羊羊", "沸羊羊", "灰太狼", "紅太狼", "小灰灰");
        
        Set<String> set = stream.collect(Collectors.toSet());
        System.out.println(set);
    }
}

Stream流轉化爲數組

轉化爲數組:stream.toArray()
返回值爲Object類型

public class Demo02 {
    public static void main(String[] args) {
        //創建一個Stream流
        Stream<String> stream = Stream.of("美羊羊", "喜羊羊", "慢羊羊", "懶羊羊", "沸羊羊", "灰太狼", "紅太狼", "小灰灰");
        //使用Stream流中的方法toArray
        Object[] arr = stream.toArray();
        for (Object o : arr) {
            System.out.println(o);
        }
    }
}

Stream流綜合案例

需求:
現在有兩個 ArrayList 集合存儲隊伍當中的多個成員姓名,要求使用Stream流進行以下若干操作步驟:

  1. 第一個隊伍只要名字爲3個字的成員姓名;
  2. 第一個隊伍篩選之後只要前3個人;
  3. 第二個隊伍只要姓張的成員姓名;
  4. 第二個隊伍篩選之後不要前2個人;
  5. 將兩個隊伍合併爲一個隊伍;
  6. 根據姓名創建 Person 對象;
  7. 打印整個隊伍的Person對象信息。
public class Demo01 {
    public static void main(String[] args) {
        List<String> one = new ArrayList<>();
        one.add("迪麗熱巴");
        one.add("宋遠橋");
        one.add("蘇星河");
        one.add("老子");
        one.add("莊子");
        one.add("孫子");
        one.add("洪七公");

        //1. 第一個隊伍只要名字爲3個字的成員姓名;
        //2.  第一個隊伍篩選之後只要前3個人;
        //把集合轉化成流
        Stream<String> oneStream = one.stream().filter(name -> name.length() == 3).limit(3);
        List<String> two = new ArrayList<>();
        two.add("古力娜扎");
        two.add("張無忌");
        two.add("張三丰");
        two.add("趙麗穎");
        two.add("張二狗");
        two.add("張天愛");
        two.add("張三");
        //3.  第二個隊伍只要姓張的成員姓名;
        //4.  第二個隊伍篩選之後不要前2個人;
        //把集合轉化爲Stream流
        Stream<String> twoStream = two.stream().filter(name -> name.startsWith("張")).skip(2);
        //5.  將兩個隊伍合併爲一個隊伍;
        //6. 根據姓名創建 Person 對象;
        //7. 打印整個隊伍的Person對象信息。
        Stream.concat(oneStream,twoStream).map(Person::new).forEach(System.out::println);
    }
}
發佈了25 篇原創文章 · 獲贊 33 · 訪問量 5958
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章