1?? 概念及特征
Java的Stream流是在Java 8中引入的一種用于處理集合數(shù)據(jù)的功能強大且易于使用的工具,旨在簡化集合框架的操作。它的設(shè)計目的是為了提供一種更簡潔、更靈活和更可讀的方式來處理集合數(shù)據(jù)。
在之前,我們通常使用迭代器或循環(huán)來遍歷和操作集合元素,這種方式容易出錯且代碼冗長。Java 8通過引入Stream流來解決這個問題,提供了一種函數(shù)式編程風格的集合操作方法。
Stream流是對集合進行操作的高級抽象,可以將集合看作是一種源(source),而Stream表示這個源上進行的計算操作序列。 通過使用Stream API,我們可以以流水線方式處理數(shù)據(jù),并進行各種轉(zhuǎn)換和聚合操作。
在Java中,Stream流分為兩種類型:
- 流(Stream):表示順序流,按照數(shù)據(jù)源的順序進行操作,適用于串行操作。
- 并行流(ParallelStream):表示并行流,可以同時對數(shù)據(jù)源的多個元素進行操作,適用于并行計算。
Stream流具有以下特點:
- 流是一次性的:流不會保存元素,它僅僅描述了操作的序列,并且在執(zhí)行聚合操作之后就被消耗掉了。即使我們對一個流執(zhí)行多個操作,每個操作也只會在需要輸出結(jié)果時才會執(zhí)行,并且在執(zhí)行完畢后,流不能再次使用。這與傳統(tǒng)的集合不同,集合可以隨時進行增刪元素的操作;
- 流是無狀態(tài)的:流的操作不會修改原始數(shù)據(jù)結(jié)構(gòu),而是通過創(chuàng)建一個新的流來執(zhí)行操作,并最終返回一個結(jié)果。原始數(shù)據(jù)結(jié)構(gòu)保持不變。這種無狀態(tài)的特性使得流操作可以并行處理數(shù)據(jù),不用擔心多線程下的數(shù)據(jù)競爭問題;
- 流是延遲執(zhí)行的:流的操作被稱為延遲執(zhí)行,也就是說,在流的聚合操作被觸發(fā)之前,中間操作不會立即執(zhí)行。這意味著我們可以先構(gòu)建一個復雜的流操作鏈,然后在需要結(jié)果的時候才觸發(fā)最終的操作。這種延遲執(zhí)行的機制有助于優(yōu)化性能,避免不必要的計算。
Stream流的實現(xiàn)原理主要基于迭代器和函數(shù)式編程的思想。在內(nèi)部迭代的過程中,流通過一系列操作進行鏈式處理,將每個元素傳遞給下一個操作,并最終生成結(jié)果。
在并行流的情況下,流將輸入數(shù)據(jù)分成多個小塊,分配給不同的線程并行處理。處理完后,再合并結(jié)果并返回。
2?? 優(yōu)勢和缺點
Stream流具有以下優(yōu)點:
- 簡潔:使用流的聚合操作可以極大地減少代碼量;
- 高效:流的并行操作可以利用多核處理器提高運行效率;
- 函數(shù)式編程:流的操作方法遵循函數(shù)式編程的思想,使代碼更加簡潔、易讀和可維護;
- 可復用:可以使用復合操作將多個流操作鏈在一起。
然而,Stream流也有一些缺點:
- 可讀性降低:對于復雜的操作,使用Stream可能比傳統(tǒng)的循環(huán)方式可讀性稍差;
- 一次性使用:一旦流被使用過,就不能再次使用,需要重新創(chuàng)建一個新的流;
-
可能會影響性能:雖然并行流可以提高運行效率,但在某些情況下,額外的分組和合并操作可能會造成性能下降。
3?? 使用
3.1 語法
Stream提供了兩種類型的操作:中間操作和終端操作。中間操作用于鏈式調(diào)用,并可以有多個,而終端操作是觸發(fā)計算的地方。
而使用Stream主要分為三個步驟:
-
創(chuàng)建流:也即獲取一個Stream對象,可以通過集合、數(shù)組或者其他方式創(chuàng)建一個Stream。如可以使用
Stream.of()
方法創(chuàng)建流; -
進行中間操作:對Stream進行連續(xù)的中間操作,包括過濾、映射、排序、去重等處理。如可以使用
forEach()
方法遍歷流中的元素,并使用filter()
、map()
、sorted()
等方法對流進行操作; -
執(zhí)行終結(jié)操作:最后使用一個終結(jié)操作來觸發(fā)計算并產(chǎn)生結(jié)果,如收集、聚合、遍歷等。如可以使用
reduce()
方法進行元素的歸約操作,使用collect()
方法進行元素的收集操作。
3.2 常用API詳解
Stream API提供了豐富的操作方法,可根據(jù)不同的需求靈活選擇。常用的操作API有:
-
Intermediate操作:如
filter()
、map()
、sorted()
,用于對元素進行篩選、映射、排序等操作。 -
Terminal操作:如
forEach()
、count()
、collect()
,用于對流進行最終的輸出、統(tǒng)計和收集操作。 -
Short-circuiting操作:如
findFirst()
、anyMatch()
、allMatch()
,用于在滿足條件時立即終止流的操作。
以下是一些Stream操作API詳情列表:
類型 | 方法 | 作用 |
---|---|---|
中間操作 | filter(Predicate) | 過濾符合條件的元素 |
map(Function) | 對每個元素應用轉(zhuǎn)換函數(shù) | |
flatMap(Function) | 將每個元素轉(zhuǎn)換成Stream對象,然后將所有的Stream連接成一個Stream | |
distinct() | 去除重復的元素 | |
sorted([Comparator]) | 排序元素,默認為自然排序 | |
limit(n) | 截取指定數(shù)量的元素 | |
skip(n) | 跳過指定數(shù)量的元素 | |
peek(Consumer) | 對每個元素執(zhí)行操作,不影響流中的其他元素 | |
takeWhile(Predicate) | 從開頭開始連續(xù)取元素滿足指定條件,直到遇到不滿足條件的元素 | |
dropWhile(Predicate) | 從開頭開始連續(xù)跳過元素滿足指定條件,直到遇到不滿足條件的元素 | |
終結(jié)操作 | collect(Collector) | 將流轉(zhuǎn)換為集合或其他數(shù)據(jù)結(jié)構(gòu) |
forEach(Consumer) | 遍歷流中的元素,并對其執(zhí)行操作 | |
reduce(BinaryOperator) | 使用給定的二元操作符將元素歸約成一個值 | |
max([Comparator]) | 找出流中的最大值 | |
min([Comparator]) | 找出流中的最小值 | |
toArray() | 將流中的元素轉(zhuǎn)換為數(shù)組 | |
count() | 統(tǒng)計流中的元素數(shù)量 | |
findFirst() | 返回滿足條件的第一個元素 | |
findAny() | 返回任意滿足條件的元素 | |
anyMatch(Predicate) | 判斷流中是否存在任意一個元素滿足給定條件 | |
allMatch(Predicate) | 判斷流中所有元素是否都滿足給定條件 | |
noneMatch(Predicate) | 判斷流中是否沒有任何元素滿足給定條件 |
3.3 案例
下面是一個簡單的Java程序,演示了上述所有方法的使用:
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class StreamOperationsDemo {
public static void main(String[] args) {
// 創(chuàng)建一個包含整數(shù)的集合
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 1, 2, 3, 4, 5);
// filter: 過濾掉大于3的元素
List<Integer> filteredList = numbers.stream()
.filter(num -> num <= 3)
.collect(Collectors.toList());
System.out.println("Filtered List: " + filteredList);
// map: 將每個元素乘以2
List<Integer> mappedList = numbers.stream()
.map(num -> num * 2)
.collect(Collectors.toList());
System.out.println("Mapped List: " + mappedList);
// flatMap: 將每個元素轉(zhuǎn)換成Stream對象,然后將所有的Stream連接成一個Stream
List<String> words = Arrays.asList("Hello", "World");
List<String> flatMappedList = words.stream()
.flatMap(word -> Arrays.stream(word.split("")))
.collect(Collectors.toList());
System.out.println("FlatMapped List: " + flatMappedList);
// distinct: 去除重復的元素
List<Integer> distinctList = numbers.stream()
.distinct()
.collect(Collectors.toList());
System.out.println("Distinct List: " + distinctList);
// sorted: 對元素進行排序
List<Integer> sortedList = numbers.stream()
.sorted()
.collect(Collectors.toList());
System.out.println("Sorted List: " + sortedList);
// limit: 截取指定數(shù)量的元素
List<Integer> limitedList = numbers.stream()
.limit(3)
.collect(Collectors.toList());
System.out.println("Limited List: " + limitedList);
// skip: 跳過指定數(shù)量的元素
List<Integer> skippedList = numbers.stream()
.skip(3)
.collect(Collectors.toList());
System.out.println("Skipped List: " + skippedList);
// peek: 對每個元素執(zhí)行操作,不影響流中的其他元素
List<Integer> peekedList = numbers.stream()
.peek(num -> System.out.println("Peeking element: " + num))
.collect(Collectors.toList());
// takeWhile: 從開頭開始連續(xù)取元素滿足條件,直到遇到不滿足條件的元素
List<Integer> takenList = numbers.stream()
.takeWhile(num -> num < 4)
.collect(Collectors.toList());
System.out.println("Taken List: " + takenList);
// dropWhile: 從開頭開始連續(xù)跳過元素滿足條件,直到遇到不滿足條件的元素
List<Integer> droppedList = numbers.stream()
.dropWhile(num -> num < 4)
.collect(Collectors.toList());
System.out.println("Dropped List: " + droppedList);
// collect: 將流轉(zhuǎn)換為集合或其他數(shù)據(jù)結(jié)構(gòu)
List<Integer> collectedList = numbers.stream()
.collect(Collectors.toList());
System.out.println("Collected List: " + collectedList);
// forEach: 遍歷流中的元素,并對其執(zhí)行操作
numbers.stream()
.forEach(System.out::println);
// reduce: 使用給定的二元操作符將元素歸約成一個值
int sum = numbers.stream()
.reduce(0, Integer::sum);
System.out.println("Sum: " + sum);
// max: 找出流中的最大值
int max = numbers.stream()
.max(Integer::compare)
.orElse(-1);
System.out.println("Max: " + max);
// min: 找出流中的最小值
int min = numbers.stream()
.min(Integer::compare)
.orElse(-1);
System.out.println("Min: " + min);
// toArray: 將流中的元素轉(zhuǎn)換為數(shù)組
Integer[] array = numbers.stream()
.toArray(Integer[]::new);
System.out.println("Array: " + Arrays.toString(array));
// count: 統(tǒng)計流中的元素數(shù)量
long count = numbers.stream()
.count();
System.out.println("Count: " + count);
// findFirst: 返回滿足條件的第一個元素
int first = numbers.stream()
.findFirst()
.orElse(-1);
System.out.println("First: " + first);
// findAny: 返回任意滿足條件的元素
int any = numbers.stream()
.findAny()
.orElse(-1);
System.out.println("Any: " + any);
// anyMatch: 判斷流中是否存在任意一個元素滿足給定條件
boolean anyMatch = numbers.stream()
.anyMatch(num -> num % 2 == 0);
System.out.println("Any Match: " + anyMatch);
// allMatch: 判斷流中所有元素是否都滿足給定條件
boolean allMatch = numbers.stream()
.allMatch(num -> num % 2 == 0);
System.out.println("All Match: " + allMatch);
// noneMatch: 判斷流中是否沒有任何元素滿足給定條件
boolean noneMatch = numbers.stream()
.noneMatch(num -> num > 10);
System.out.println("None Match: " + noneMatch);
}
}
這個程序演示了如何使用Stream的中間操作和終端操作。
程序的運行結(jié)果如下:
Filtered List: [1, 2, 3, 1, 2, 3]
Mapped List: [2, 4, 6, 8, 10, 2, 4, 6, 8, 10]
FlatMapped List: [H, e, l, l, o, W, o, r, l, d]
Distinct List: [1, 2, 3, 4, 5]
Sorted List: [1, 1, 2, 2, 3, 3, 4, 4, 5, 5]
Limited List: [1, 2, 3]
Skipped List: [4, 5, 1, 2, 3, 4, 5]
Peeking element: 1
Peeking element: 2
Peeking element: 3
Peeking element: 4
Peeking element: 5
Peeking element: 1
Peeking element: 2
Peeking element: 3
Peeking element: 4
Peeking element: 5
Taken List: [1, 2, 3]
Dropped List: [4, 5, 1, 2, 3, 4, 5]
Collected List: [1, 2, 3, 4, 5, 1, 2, 3, 4, 5]
1
2
3
4
5
1
2
3
4
5
Sum: 30
Max: 5
Min: 1
Array: [1, 2, 3, 4, 5, 1, 2, 3, 4, 5]
Count: 10
First: 1
Any: 1
Any Match: true
All Match: false
None Match: true
4?? 應用場景
Stream流廣泛應用于數(shù)據(jù)處理、集合操作、并行計算等場景。它可以使代碼更簡潔、易讀和具有可維護性,同時充分發(fā)揮多核處理器的計算能力。如下:
- 數(shù)據(jù)分析:根據(jù)條件過濾出需要的數(shù)據(jù),并進行統(tǒng)計、匯總或生成報表;
- 數(shù)據(jù)處理:對大規(guī)模、復雜的數(shù)據(jù)集合進行篩選、轉(zhuǎn)換、排序以及聚合和分組等;
- 數(shù)據(jù)查詢:通過多個中間操作構(gòu)建復雜的查詢條件,獲取符合要求的數(shù)據(jù);
-
并行處理:在多核處理器上可利用
parallel
方法實現(xiàn)并行處理大批量數(shù)據(jù),提高系統(tǒng)的處理性能。
例如,對于一個電商平臺的訂單數(shù)據(jù),我們可以使用流來實現(xiàn)以下功能:
- 篩選出所有金額大于1000的訂單;
- 將訂單按照金額從高到低進行排序;
- 獲取前5個訂單的信息;
- …
5?? 使用技巧
要使用流,首先需要從數(shù)據(jù)源創(chuàng)建一個流,然后通過一系列的中間操作和終端操作來對流進行處理和操作。
在使用流時,可以注意以下幾點優(yōu)化技巧:
- 合理選擇流的類型,根據(jù)實際情況選擇順序流或并行流,對于大數(shù)據(jù)集合,考慮使用并行流以提高性能;
- 盡量減少中間操作的數(shù)量,合并多個操作可以減少迭代次數(shù);
- 盡量避免使用短路操作,以充分發(fā)揮并行流的優(yōu)勢;
- 使用延遲執(zhí)行的特性,只在需要獲取結(jié)果時觸發(fā)終端操作。
6?? 并行流 ParallelStream
并行流(ParallelStream)允許在多線程環(huán)境下并發(fā)地執(zhí)行操作,從而提高處理大數(shù)據(jù)集的效率。
ParallelStream類在Java中沒有特有的方法。它與普通的Stream類具有相同的操作方法,可以使用 filter
、map
、flatMap
、distinct
、sorted
、limit
、skip
、peek
等方法。這些方法可以在并行流上執(zhí)行,并發(fā)地處理數(shù)據(jù)。并行流會自動將數(shù)據(jù)分成多個部分,并在多個線程上同時進行處理,加快了處理速度。
需要注意的是,在使用并行流時,應該要注意線程安全和性能問題。如果并行執(zhí)行的操作具有共享狀態(tài)、副作用或依賴于元素之間的順序,那么可能會導致不正確的結(jié)果。并行流適用于對大量元素進行計算密集型操作,但并不適用于有狀態(tài)或依賴前后元素的操作。因此,在使用并行流時,需要確保操作的可靠性,并在必要時使用同步措施來保證線程安全。
除了以上普通的Stream操作方法,在并行流中還可以使用.parallel()
和.sequential()
方法切換并行流和順序流的操作模式。.parallel()
方法將流轉(zhuǎn)換為并行流,允許并發(fā)地對元素進行操作。而.sequential()
方法則將并行流轉(zhuǎn)回為順序流,僅使用單線程順序地處理元素。
?? 總結(jié)
Java Stream流為我們提供了一種簡潔而強大的方式來操作數(shù)據(jù)集合。它具有許多優(yōu)點,如簡化操作、惰性求值和并行處理。同時也有一些缺點,如學習成本稍高和可讀性稍差。然而,在正確使用和優(yōu)化Stream的情況下,可以極大地提高代碼的可讀性和維護性,并實現(xiàn)更高效的數(shù)據(jù)處理與計算。
通過使用流,我們可以以更直觀、簡潔的方式對數(shù)據(jù)進行處理和操作,并發(fā)揮多核處理器的計算能力。
然而,使用流也需要注意數(shù)據(jù)量、性能和適用場景等因素。最重要的是根據(jù)具體情況選擇合適的流類型,并根據(jù)實際需求合理組合流的操作,以實現(xiàn)更高效、可讀性更好的代碼。

到了這里,關(guān)于【Java基礎(chǔ)教程】(三十)Java新特性篇 · 第十講: Stream流——釋放流式編程的效率與優(yōu)雅,狂肝萬字只為透徹講清 Stream流!~的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!