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
沒有留言:
張貼留言