Stream 是 Java 8 引入的一種新的抽象,用於處理集合類 (Collection) 的數據。Stream 並不存儲數據,而是按需計算數據。Stream 操作有兩個重要特性:
- 流水線操作 (Pipelining):Stream 操作可以鏈式調用,形成一個流水線,這些操作既可以是中間操作(intermediate operation),也可以是終端操作(terminal operation)。
- 內部迭代 (Internal Iteration):不同於集合類的外部迭代 (external iteration),Stream 使用內部迭代,通過底層的迭代器實現。
Stream 的優點
- 簡潔性:Stream API 提供了聲明性的方法鏈來處理數據,這使得代碼更簡潔、更易讀。
- 易於並行化:Stream API 提供了簡單的並行化處理數據的方式,通過 parallelStream 可以輕鬆實現並行計算,提高處理性能。
- 函數式編程風格:Stream API 支持函數式編程,允許使用 Lambda 表達式和方法引用,減少了樣板代碼。
- 延遲執行:中間操作是惰性求值的,只有在終端操作執行時才會計算,優化了性能。
-
更高的抽象層次:Stream API 提供了一種更高層次的數據處理抽象,使得代碼更具表達力和可維護性。
用途
Stream API 主要用於對集合數據進行操作,比如過濾、排序、映射、歸約等。它提供了一種函數式編程的風格,使代碼更簡潔、易讀、可維護。常見用途包括:
- 過濾 (Filtering):從集合中篩選出符合條件的元素。
- 映射 (Mapping):將集合中的每個元素映射成另一種形式。
- 歸約 (Reduction):將集合中的元素組合成一個值。
- 收集 (Collecting):將處理後的數據轉換為其他集合形式。
- 統計 (Statistics):對數據進行統計計算,如計數、求和、平均值等。
常用的 Stream 操作示例:
1. 創建 Stream
List<String> list = Arrays.asList("a", "b", "c", "d");
// 從集合創建 Stream
Stream<String> stream = list.stream();
// 從數組創建 Stream
Stream<String> stream = Stream.of("a", "b", "c", "d");
2. 中間操作 (Intermediate Operations)
中間操作會返回一個新的 Stream,它們是惰性求值的,只有在終端操作執行時才會執行。
// 過濾
Stream<String> filteredStream = stream.filter(s -> s.startsWith("a"));
// 映射
Stream<String> mappedStream = stream.map(String::toUpperCase);
// 排序
Stream<String> sortedStream = stream.sorted();
// 去重
Stream<String> distinctStream = stream.distinct();
// 排序
Stream<String> sortedStream = stream.sorted();
Stream<String> sortedByComparator = stream.sorted((s1, s2) -> s2.compareTo(s1));
3. 終端操作 (Terminal Operations)
終端操作會觸發 Stream 的計算,並生成一個結果或副作用。
// 收集
List<String> collectedList = stream.collect(Collectors.toList());
// 統計
long count = stream.count();
// 查找
Optional<String> firstElement = stream.findFirst();
//reduce:歸約操作,將元素組合成一個值
Optional<String> concatenated = stream.reduce((s1, s2) -> s1 + s2);
代碼案例
案例 1:過濾和映射
List<String> strings = Arrays.asList("apple", "banana", "orange", "apple", "mango");
List<String> result = strings.stream() //獲取stream對象
.filter(s -> s.startsWith("a")) //過濾,檢查列表中是否以“a”開頭,滿足條件保留到流中
.map(String::toUpperCase) //映射,將流中每個字符串轉換為大寫
.collect(Collectors.toList()); //收集,將流中的元素收集到一個list中
System.out.println(result); // 輸出:[APPLE, APPLE]
案例 2:排序和去重
List<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5);
List<Integer> result = numbers.stream()
.distinct() //去重
.sorted() //排序
.collect(Collectors.toList());
System.out.println(result); // 輸出:[2, 3, 5, 7]
案例 3:歸約操作
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
int sum = numbers.stream()
.reduce(0, Integer::sum); //reduce:歸約操作,將元素組合成一個值
System.out.println(sum); // 輸出:15
案例 4:分組操作
List<String> strings = Arrays.asList("apple", "banana", "orange", "apple", "mango");
Map<String, Long> result = strings.stream()
.collect(Collectors.groupingBy(s -> s, Collectors.counting()));
//終端操作,收集 使用Collectors.groupingBy()方法來收集流中的元素。
// 這個方法接受兩個參數:一個是分類函數(在這裏是s -> s,表示使用字符串本身作為鍵)
// 另一個是下游的收集器(在這裏是Collectors.counting(),用於計算每個鍵的出現次數)。
System.out.println(result); // 輸出:{orange=1, banana=1, apple=2, mango=1}
案例 5:並行流
List<String> strings = Arrays.asList("apple", "banana", "orange", "apple", "mango");
List<String> result = strings.parallelStream() //並行處理集合中的數據,適用與大規模的計算、數據轉換、過濾等操作
.filter(s -> s.startsWith("a"))
.map(String::toUpperCase)
.sorted()
.collect(Collectors.toList());
System.out.println(result); // 輸出:[APPLE, APPLE]
案例 6:生成數列
List<Integer> numbers = IntStream.range(1, 10) //創建了一個從1到9的整數序列的IntStream
.boxed() //將IntStream中的原始類型int轉換成Integer對象的Stream
.collect(Collectors.toList());
System.out.println(numbers); // 輸出:[1, 2, 3, 4, 5, 6, 7, 8, 9]
案例 7:字符串拼接
List<String> strings = Arrays.asList("apple", "banana", "orange");
String result = strings.stream()
.collect(Collectors.joining(", ")); //字符串拼接
System.out.println(result); // 輸出:apple, banana, orange
注意事項
1.排序 stream.sorted()
Stream<String> sortedStream = stream.sorted();
Stream<String> sortedByComparator = stream.sorted((s1, s2) -> s2.compareTo(s1));
- 無參的 sorted():元素按照它們的自然順序排序。如果你的元素是數字或字符串等基本類型,它們將按照自然數值或字典順序排序。如果你的元素是對象,它們將按照 Comparable 接口中定義的順序排序。
- 帶Comparator的 sorted():元素按照你提供的 Comparator 排序。這允許你定製排序邏輯,例如,你可以按字符串長度排序、按某個屬性的逆序排序等。
2.中間操作 (Intermediate Operations) 和 終端操作 (Terminal Operations) 的區別
-
中間操作:
- 返回一個新的 Stream。
- 惰性求值:中間操作本身不會觸發實際的計算,只有在終端操作執行時才會計算。
- 例子:filter, map, flatMap, distinct, sorted, limit, skip, peek。
-
終端操作:
- 觸發 Stream 的計算,並生成結果。
- 不是惰性求值,一旦調用終端操作,整個 Stream 的計算就會執行。
- 終端操作會關閉 Stream,之後不能再對其進行操作。
- 例子:forEach, toArray, reduce, collect, min, max, count, anyMatch, allMatch, noneMatch, findFirst, findAny。
轉載自開思通智網:https://www.opensnn.com/os/article/10000908