Java8 Stream,過分絲滑!

語言: CN / TW / HK

文章來源:https://c1n.cn/5n6fT

目錄

  • Stream 流

  • Stream 流的使用

Stream 流

Stream 流是什麼,為什麼要用它?

  • S tream是 Java8 新引入的一個包( java.util.stream),它讓我們能用宣告式的方式處理資料(集合、陣列等)。

  • Stream流式處理相較於傳統方法簡潔高效,也便於進行併發程式設計。

Stream 是 Java8 的一大亮點,是對容器物件功能的增強,它專注於對容器物件進行各種非常便利、高效的聚合操作(aggregate operation)或者大批量資料操作。

Stream 使用一種類似用 SQL 語句從資料庫查詢資料的直觀方式來提供一種對 Java 集合運算和表達的高階抽象。

Stream API 藉助於同樣新出現的 Lambda 表示式,極大的提高程式設計效率和程式可讀性。

同時,它提供序列和並行兩種模式進行匯聚操作,併發模式能夠充分利用多核處理器的優勢,使用 fork/join 並行方式來拆分任務和加速處理過程。

所以說,Java8 中首次出現的 java.util.stream 是一個函式式語言+多核時代綜合影響的產物。

Stream 流的使用

| 簡單綜合案例

問題和需求:在一個字串集合中找出以“阿”開頭的長度為 3 的字串並列印。

傳統方法:

import java.util.ArrayList;
import java.util.List;

public class Demo02NormalFilter {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("阿拉貢");
        list.add("阿爾玟");
        list.add("埃爾隆德");
        list.add("凱蘭崔爾");
        list.add("瑟蘭督伊");

        List<String> zhangList = new ArrayList<>();
        for (String name : list) {
            if (name.startsWith("阿")) {
                zhangList.add(name);
            }
        }

        List<String> shortList = new ArrayList<>();
        for (String name : zhangList) {
            if (name.length() == 3) {
                shortList.add(name);
            }
        }
        for (String name : shortList) {
            System.out.println(name);
        }
    }
}

這裡我們可以看到傳統的方法中含有三個迴圈,每一個作用不同:

  • 首先篩選所有姓“阿”的人

  • 然後篩選名字有三個字的人

  • 最後進行對結果進行列印輸出

這樣的處理過程程式碼冗長,導致程式碼可讀性較差,效率也比較低。而使用 Stream 來進行處理就能使程式碼優雅地多。

Stream 流式處理方法:

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;

public class Demo02NormalFilter {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("阿拉貢");
        list.add("阿爾玟");
        list.add("埃爾隆德");
        list.add("凱蘭崔爾");
        list.add("瑟蘭督伊");

        list.stream()
            .filter(s ‐> s.startsWith("阿"))
            .filter(s ‐> s.length() == 3)
            .forEach(System.out::println);
    }
}

利用 Stream 流中的方法再結合函式式介面和 Lambda 表示式,我們的程式碼就能變得格外簡潔明瞭。

| 獲取流

①根據 Collection 獲取流

首先,java.util.Collection 介面中加入了 default 方法 stream 用來獲取流,所以其所有實現類均可獲取流。

import java.util.*;
import java.util.stream.Stream;

public class Demo04GetStream {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        Stream<String> stream1 = list.stream();

        Set<String> set = new HashSet<>();
        Stream<String> stream2 = set.stream();

        Vector<String> vector = new Vector<>();
        Stream<String> stream3 = vector.stream();
    }
}

②根據 Map 獲取流

java.util.Map 介面不是 Collection 的子介面,且其 K-V 資料結構不符合流元素的單一特徵,所以獲取對應的流需要分 key、value 或 entry 等情況:

import java.util.HashMap;
import java.util.Map;
import java.util.stream.Stream;

public class Demo05GetStream {
    public static void main(String[] args) {
        Map<String, String> map = new HashMap<>();
        //Stream流的轉化需要單列資料,那麼我們就先把map裡面的資料變成單列的再轉化為流
        Stream<String> keyStream = map.keySet().stream();
        Stream<String> valueStream = map.values().stream();
        Stream<Map.Entry<String, String>> entryStream = map.entrySet().stream();
    }
}

③根據陣列獲取流

如果使用的不是集合或對映而是陣列,由於陣列物件不可能新增預設方法,所以 Stream 介面中提供了靜態方法 of ,使用很簡單:

import java.util.stream.Stream;

public class Demo06GetStream {
    public static void main(String[] args) {
        String[] array = { "阿拉貢", "阿爾玟", "埃爾隆德", "凱蘭崔爾","瑟蘭督伊" };
        Stream<String> stream = Stream.of(array);
    }
}

| 常用方法

流模型的操作很豐富,這裡介紹一些常用的 API。

這些方法可以被分成兩種:

  • 延遲方法: 返回值型別仍然是 Stream 介面自身型別的方法,因此支援鏈式呼叫。(除了終結方法外,其餘方法均為延遲方法) 

  • 終結方法: 返回值型別不再是 Stream 介面自身型別的方法,因此不再支援類似 StringBuilder 那樣的鏈式呼叫。( 如果想知道更多 方法,建議自行參考 API 文件

接下來我會介紹兩個終結方法 count 和 forEach 方法。

①逐一處理:forEach

雖然方法名字叫 forEach ,但是與 for 迴圈中的“for-each”語句是不一樣的,該方法接收一個 Consumer 介面函式,會將每一個流元素交給該函式進行處理。

方法簽名:

void forEach(Consumer<? super T> action);

基本使用:

import java.util.stream.Stream;

public class Demo12StreamForEach {
    public static void main(String[] args) {
        Stream<String> stream = Stream.of("阿拉貢", "阿爾玟", "埃爾隆德");
        stream.forEach(name‐> System.out.println(name));
    }
}

篩選:filter

可以通過 filter 方法將一個流轉換成另一個子集流。該方法接收一個 Predicate 函式式介面引數(可以是一個 Lambda 或方法引用)作為篩選條件。

方法簽名:

Stream<T> filter(Predicate<? super T> predicate);

基本使用:

import java.util.stream.Stream;

public class Demo07StreamFilter {
    public static void main(String[] args) {
        Stream<String> original = Stream.of("阿拉貢", "阿爾玟", "埃爾隆德");
        Stream<String> result = original.filter(s ‐> s.startsWith("阿"));
    }
}

在這裡通過 Lambda 表示式來指定了篩選的條件:必須以“阿”開頭。

對映:map

如果需要將流中的元素對映到另一個流中,可以使用 map 方法。該介面需要一個 Function 函式式介面引數,可以將當前流中的 T 型別資料轉換為另一種 R 型別的流。

方法簽名:

<R> Stream<R> map(Function<? super T, ? extends R> mapper);

基本使用:

import java.util.stream.Stream;

public class Demo08StreamMap {
    public static void main(String[] args) {
        Stream<String> original = Stream.of("10", "12", "18");
        Stream<Integer> result = original.map(str‐>Integer.parseInt(str));
    }
}

這段程式碼中, map 方法的引數通過方法引用,將字串型別轉換成為了 int 型別(並自動裝箱為 Integer 類物件)。

統計個數:count

正如舊集合 Collection 當中的 size 方法一樣,流提供 count 方法來數一數其中的元素個數。該方法返回一個 long 值代表元素個數(不再像舊集合那樣是 int 值)。

方法簽名:

long count();

基本使用:

import java.util.stream.Stream;

public class Demo09StreamCount {
    public static void main(String[] args) {
        Stream<String> original = Stream.of("阿拉貢", "阿爾玟", "埃爾隆德");
        Stream<String> result = original.filter(s ‐> s.startsWith("阿"));
        System.out.println(result.count()); // 2
    }
}

取用前幾個:limit

limit 方法可以對流進行擷取,只取用前 n 個。引數是一個 long 型,如果集合當前長度大於引數則進行擷取;否則不進行操作。

方法簽名:

Stream<T> limit(long maxSize);

基本使用:

import java.util.stream.Stream;

public class Demo10StreamLimit {
    public static void main(String[] args) {
        Stream<String> original = Stream.of("阿拉貢", "阿爾玟", "埃爾隆德");
        Stream<String> result = original.limit(2);
        System.out.println(result.count()); // 2
    }
}

跳過前幾個:skip

如果希望跳過前幾個元素,可以使用 skip 方法獲取一個擷取之後的新流。如果流的當前長度大於 n,則跳過前 n 個;否則將會得到一個長度為 0 的空流。

方法簽名:

Stream<T> skip(long n);

基本使用:

import java.util.stream.Stream;

public class Demo11StreamSkip {
    public static void main(String[] args) {
        Stream<String> original = Stream.of("阿拉貢", "阿爾玟", "埃爾隆德");
        Stream<String> result = original.skip(2);
        System.out.println(result.count()); // 1
    }
}

②組合:concat

如果有兩個流,希望合併成為一個流,那麼可以使用 Stream 介面的靜態方法 concat。

這是一個靜態方法,與 java.lang.String 當中的 concat 方法是不同的。

方法簽名:

static <T> Stream<T> concat(Stream<? extends T> a, Stream<? extends T>b)

基本使用:

import java.util.stream.Stream;

public class Demo12StreamConcat {
    public static void main(String[] args) {
        Stream<String> streamA = Stream.of("阿拉貢");
        Stream<String> streamB = Stream.of("阿爾玟");
        Stream<String> result = Stream.concat(streamA, streamB);
    }
}

-------------  END  -------------

掃碼 免費 獲取 600+頁 石杉老師原創精品文章彙總PDF

原創技術文章彙總

點個 在看 你最好看