什麼是 Stream?
Stream(流)是一個來自數據源的元素隊列並支持聚合操作
元素隊列 數據是以一系列元素的形式存在的,按照某種順序排列,形成一個隊列。在流的概念中,這些元素通常是連續到達的,可以逐個處理,而不必一次性加載整個數據集到內存中。
數據源 流的來源。 可以是集合,數組,I/O channel, 產生器generator 等。
聚合操作 對一系列元素執行計算以生成單個彙總值的過程。例如,計算流中所有元素的平均值、總和、最大值、最小值等。
一、Stream的特點
- 無存儲:Stream本身不存儲數據,它更像是一個高級迭代器,通過一系列流水線操作來處理數據。數據仍然存儲在原始的數據源(如集合、數組等)中。
- 延遲執行:Stream中的操作是惰性執行的,只有在需要結果時(即觸發終端操作時)才會實際執行。這允許通過多箇中間操作組合複雜的查詢,而無需立即計算中間結果。
- 不可變性:每次對Stream的操作都會返回一個新的Stream對象,而不會修改原始數據源。
- 並行處理能力:Stream支持並行流(parallelStream),允許在多核處理器上並行處理數據,提升處理性能。
Demo演示
不存儲數據,不改變數據源
@Test
void demo() {
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
System.out.println(numbers); // 輸出 [1, 2, 3, 4, 5]
// 創建一個Stream
Stream<Integer> numberStream = numbers.stream();
// 看看numberStream的內容
System.out.println(numberStream); // 輸出 java.util.stream.ReferencePipeline$Head@2ecf5915
// 原始數據源仍然存在且未改變
System.out.println("Original numbers: " + numbers); // 輸出 [1, 2, 3, 4, 5]
// 使用Stream進行操作(例如打印)
numberStream.forEach(System.out::println); // 1 2 3 4 5
// 再次確認原始數據源未改變
System.out.println("Original numbers after using stream: " + numbers); // 輸出 [1, 2, 3, 4, 5]
}
延時執行
@Test
void demo() {
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
Stream<Integer> filteredStream = numbers.stream()
.filter(n -> {
System.out.println("filter" + n); // 注意打印時間
return n > 2;
});
// 未執行終端操作執行完畢後
System.out.println("Before forEach operation");
// 觸發終端操作
filteredStream.forEach(System.out::println);
// 終端操作執行完畢後
System.out.println("After forEach operation");
}
二、創建 Stream 流
順序流和並行流
順序流
- 是一種單線程的流。
- 它按照數據流的順序依次處理每個元素,每個元素的處理都必須等待上一個元素的處理完成才能開始。
- 使用順序流可以保證數據處理的順序和一致性。
- 適用於處理數據量較小的情況。
並行流
- 是一種多線程的流。
- 它可以將數據分成多個部分並行處理,每個部分都可以在不同的線程中處理,從而提高處理效率。
- 使用並行流可以提高數據處理的速度,適用於處理數據量較大、處理時間較長的情況。
- 但是並行流也有一些缺點,比如線程之間的通信和同步會帶來額外的開銷,而且並行流可能會影響數據的順序和一致性。
創建流
從集合創建流
List<String> list = Arrays.asList("a", "b", "c");
Stream<String> stream = list.stream(); // 創建一個順序流
Stream<String> parallelStream = list.parallelStream(); // 創建一個並行流
從數組建流
int[] array = {1, 2, 3, 4, 5};
IntStream intStream = Arrays.stream(array); // 創建一個IntStream順序流
IntStream intParallelStream = Arrays.stream(array).parallel(); // 創建一個IntStream並行流
從值創建流
Stream<String> stream = Stream.of("a", "b", "c"); // 創建一個順序流
Stream<Integer> numberStream = Stream.of(1, 2, 3, 4, 5).parallel(); // 創建一個並行流
......
note
順序流:可以使用集合類的stream()方法、Arrays類的stream()方法、Stream類的of()方法、Stream類的iterate()方法、Stream類的generate()方法、Files類的lines()方法等來獲取順序流。
並行流:可以使用集合類的parallelStream()方法、Stream類的of()方法的parallel()方法、Stream類的iterate()方法的parallel()方法、Stream類的generate()方法的parallel()方法等來獲取並行流。
三、Stream的操作類型
Stream的操作可以分為中間操作和終端操作兩類。
中間操作
返回一個新的Stream對象,允許多個操作鏈式調用。常見的中間操作有
- filter(Predicate<? super T> predicate):根據給定的條件過濾出符合條件的元素。
@Test
void demo() {
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6);
Stream<Integer> filteredStream = numbers.stream()
.filter(n -> n % 2 == 0);
System.out.println(filteredStream); // 輸出 java.util.stream.ReferencePipeline$2@5b5a4aed
filteredStream.forEach(System.out::println); // 輸出 2 4 6
}
- map(Function<? super T, ? extends R> mapper):將每個元素通過給定的函數映射成另一個元素。
- sorted():對Stream中的元素進行自然排序(針對實現了Comparable接口的元素)。
- distinct():去除Stream中的重複元素(依賴元素的hashCode和equals方法)。
@Test
void demo() {
List<Integer> numbers = Arrays.asList(5, 3, 8, 3, 1, 5, 9, 2, 6, 7);
List<String> sortedDistinctMappedNumbers = numbers.stream()
// 將每個整數映射為 numA 表示形式
.map(num -> num.toString() + "A")
// 去重
.distinct()
// 排序
.sorted()
// 收集結果到一個新的列表中
.collect(Collectors.toList());
// 打印結果
System.out.println(sortedDistinctMappedNumbers); // 輸出 [1A, 2A, 3A, 5A, 6A, 7A, 8A, 9A]
}
- limit(long maxSize):限制Stream中元素的數量。
- skip(long n):跳過Stream中的前n個元素。
- peek(Consumer<? super T> action):對流中的每個元素執行操作並返回一個新的流,主要用於調試。
終端操作
觸發Stream的處理並生成結果。常見的終端操作有:
- forEach(Consumer<? super T> action):對Stream中的每個元素執行給定的操作。
- collect(Collector<? super T, A, R> collector):將Stream中的元素收集到一個新的集合中。
- reduce(BinaryOperator<T> accumulator):對Stream中的元素進行歸約操作,可以實現求和、求最大值、求最小值等操作。
@Test
void demo() {
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
int sum = numbers.stream()
// 使用reduce方法進行求和
.reduce(0, Integer::sum); // 初始值為0,累加器為Integer::sum
System.out.println(sum); // 輸出 15
}
- count():返回Stream中元素的數量。
- min(Comparator<? super T> comparator):返回Stream中的最小元素。
- max(Comparator<? super T> comparator):返回Stream中的最大元素。
- anyMatch(Predicate<? super T> predicate):檢查Stream中是否存在滿足給定條件的元素。
- allMatch(Predicate<? super T> predicate):檢查Stream中的所有元素是否都滿足給定條件。
- noneMatch(Predicate<? super T> predicate):檢查Stream中的所有元素是否都不滿足給定條件。
三、Stream與迭代的對比
| Stream | 迭代 |
|---|---|
| 惰性求值 | 立即求值 |
| 可鏈式調用函數 | 通常使用的是for或forEach |
| 聲明式的:告訴程序你想要什麼結果 | 命令式:告訴程序如何執行每一步操作 |
總結
中間操作: 返回的是一個Stream類型的對象,允許多個操作鏈式調用
終端操作:能訪問數據源,不允許多個操作鏈式調用, 獲取最終的結果數據。