2021/03/16

[筆記] Effective Java #45 謹慎使用 Stream

Effective Java 3rd 簡體中文版筆記 #45 謹慎使用 Stream
Java 8 中新增 Stream API,主要有兩大關鍵 Stream (流)Stream pipeline (流管道)。Stream 中的元素可以來自任何地方,常見的像是集合、陣列、檔案及其它 Stream,Stream pipeline 中包含一個 stream,接著是 0 個或多個的中間操作 (intermediate operation) 和 1 個終止操作 (terminal operation)。每個中間操作都是將一個 stream 轉換成另一個 stream,終止操作是 stream 的最後一個運算,結果可能是收集成一個集合或是返回一個元素等。
Stream pipeline 是 lazy 的,必須在調用終止操作時才會開始運算,沒有終止操作的 stream pipeline 是不會有動作的。Stream API 是流式 (fluent) 的,可以將多個 pipeline 連接起來,如果想要 pipeline 平行運算,可以調用 parallel 方法,但通常不建議這樣使用。
List<String> list = Arrays.asList("a", "b", "c", "d", "e");
list.stream().map(String::toUpperCase)
        .forEach(System.out::println);  // ordered

list.stream().parallel().map(String::toUpperCase)
        .forEach(System.out::println);  // non-ordered
Stream API 的種類相當多,可以取代很多既有的程式寫法,但並非表示一定要使用 stream API 來取代。過度使用 stream 反而會讓程式不易閱讀及維護,因為在 lambda 寫法中,沒有方法名稱及參數類型,會降低可讀性。有時可以將 lambda 內的寫法獨立成一個方法,這樣就有方法名稱及參數類型,再讓 lambda 來引用。
Stream<String> words = ...;
words.stream().collect(
        groupingBy(word -> word.chars().sorted()
                .collect(StringBuilder::new, 
                        (sb, c) -> sb.append((char) c), 
                        StringBuilder::append).toString()))
        .values().stream().forEach(System.out::println);

// use getSomeKey() method
words.stream().collect(groupingBy(word -> getSomeKey(word)))
        .values().stream().forEach(System.out::println);

private String getSomeKey(String s) { ... }
Stream 無法在一個連接的 pipeline 中,訪問多個階段的元素,一旦 pipeline 進行到下一個階段後,上一個階段的資料就被丟棄。在撰寫程式碼時,有時候會不確定應該使用 stream 還是迭代式 (for loop、for each),開發者可以依照可讀性、維護性及慣用方式來決定,沒有具體的規定。
// Iterative
private static List<Card> newDeck() {
    List<Card> result = new ArrayList<>();
    for (Suit suit : Suit.values())
        for (Rank rank : Rank.values())
            result.add(new Card(suit, rank));
    return result;
}

// Stream-based
private static List<Card> newDeck() {
    return Stream.of(Suit.values())
            .flatMap(suit -> Stream.of(Rank.values())
                    .map(rank -> new Card(suit, rank)))
            .collect(toList());
}
轉載請註明原文網址 https://cookieandcoketw.blogspot.com/2021/03/effective-java-45-stream.html

沒有留言:

張貼留言