Java Stream 終端操作
終端操作會觸發 Stream 的執行並產生結果,每個 Stream 只能有一個終端操作。
終端操作特性
- 觸發執行:呼叫後才會執行整個 Stream pipeline
- 消耗 Stream:執行後 Stream 無法再使用
- 產生結果:回傳值或副作用
forEach - 遍歷
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
// 遍歷
names.stream().forEach(System.out::println);
// 簡化(直接使用集合的 forEach)
names.forEach(System.out::println);
// forEachOrdered(保證順序,用於平行 Stream)
names.parallelStream().forEachOrdered(System.out::println);
注意:
forEach是為了副作用,應避免修改外部狀態
collect - 收集
最常用的終端操作,將結果收集到集合:
// 收集到 List
List<String> list = stream.collect(Collectors.toList());
// 收集到 Set
Set<String> set = stream.collect(Collectors.toSet());
// 收集到特定集合
ArrayList<String> arrayList = stream.collect(
Collectors.toCollection(ArrayList::new));
// Java 16+
List<String> list = stream.toList(); // 回傳不可變 List
更多 Collectors 請參考 Java Collectors。
toArray - 轉為陣列
// 轉為 Object[]
Object[] array = stream.toArray();
// 轉為特定型別陣列
String[] strings = stream.toArray(String[]::new);
// IntStream 轉為 int[]
int[] ints = IntStream.of(1, 2, 3).toArray();
reduce - 歸約
將所有元素組合成單一結果:
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
// 有初始值
int sum = numbers.stream()
.reduce(0, (a, b) -> a + b); // 15
// 無初始值(回傳 Optional)
Optional<Integer> sum = numbers.stream()
.reduce((a, b) -> a + b);
// 三參數版本(用於平行處理)
Integer sum = numbers.parallelStream()
.reduce(0,
(partial, n) -> partial + n, // accumulator
(left, right) -> left + right); // combiner
// 常見用法
int max = numbers.stream().reduce(Integer.MIN_VALUE, Integer::max);
int min = numbers.stream().reduce(Integer.MAX_VALUE, Integer::min);
String concat = strings.stream().reduce("", (a, b) -> a + b);
count - 計數
long count = stream.count();
// 範例
long evenCount = numbers.stream()
.filter(n -> n % 2 == 0)
.count();
min / max - 最小/最大值
// 需要 Comparator
Optional<Integer> min = numbers.stream()
.min(Integer::compareTo);
Optional<Integer> max = numbers.stream()
.max(Integer::compareTo);
// 物件
Optional<User> youngest = users.stream()
.min(Comparator.comparing(User::getAge));
Optional<User> oldest = users.stream()
.max(Comparator.comparing(User::getAge));
// 基本型別 Stream 不需要 Comparator
int min = IntStream.of(1, 2, 3).min().orElse(0);
int max = IntStream.of(1, 2, 3).max().orElse(0);
findFirst / findAny - 找元素
// 找第一個
Optional<String> first = stream.findFirst();
// 找任意一個(平行時可能更快)
Optional<String> any = stream.findAny();
// 實際應用
Optional<User> user = users.stream()
.filter(u -> u.getEmail().equals(email))
.findFirst();
anyMatch / allMatch / noneMatch - 匹配
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
// 任一匹配
boolean hasEven = numbers.stream()
.anyMatch(n -> n % 2 == 0); // true
// 全部匹配
boolean allPositive = numbers.stream()
.allMatch(n -> n > 0); // true
// 沒有匹配
boolean noNegative = numbers.stream()
.noneMatch(n -> n < 0); // true
sum / average(基本型別 Stream)
// sum
int sum = IntStream.of(1, 2, 3, 4, 5).sum(); // 15
// average
OptionalDouble avg = IntStream.of(1, 2, 3, 4, 5).average(); // 3.0
// 物件需要先轉換
int totalAge = users.stream()
.mapToInt(User::getAge)
.sum();
double avgAge = users.stream()
.mapToInt(User::getAge)
.average()
.orElse(0);
summaryStatistics - 統計
IntSummaryStatistics stats = numbers.stream()
.mapToInt(Integer::intValue)
.summaryStatistics();
long count = stats.getCount();
int sum = stats.getSum();
int min = stats.getMin();
int max = stats.getMax();
double avg = stats.getAverage();
iterator / spliterator
// 轉為 Iterator
Iterator<String> it = stream.iterator();
while (it.hasNext()) {
System.out.println(it.next());
}
// Spliterator(用於自訂分割)
Spliterator<String> spliterator = stream.spliterator();
短路操作
某些操作不需要處理所有元素:
| 操作 | 說明 |
|---|---|
limit | 達到數量就停止 |
findFirst | 找到就停止 |
findAny | 找到就停止 |
anyMatch | 找到匹配就停止 |
allMatch | 找到不匹配就停止 |
noneMatch | 找到匹配就停止 |
// 只處理需要的元素
Stream.iterate(1, n -> n + 1)
.filter(n -> n % 2 == 0)
.limit(5)
.forEach(System.out::println); // 2, 4, 6, 8, 10
實際範例
計算總價
double total = orders.stream()
.flatMap(order -> order.getItems().stream())
.mapToDouble(item -> item.getPrice() * item.getQuantity())
.sum();
找出最高薪員工
Optional<Employee> topEarner = employees.stream()
.max(Comparator.comparing(Employee::getSalary));
檢查是否存在
boolean hasAdmin = users.stream()
.anyMatch(u -> u.getRole().equals("ADMIN"));
統計分析
DoubleSummaryStatistics priceStats = products.stream()
.mapToDouble(Product::getPrice)
.summaryStatistics();
System.out.println("商品數:" + priceStats.getCount());
System.out.println("最低價:" + priceStats.getMin());
System.out.println("最高價:" + priceStats.getMax());
System.out.println("平均價:" + priceStats.getAverage());
System.out.println("總價值:" + priceStats.getSum());
操作對照表
| 操作 | 回傳型別 | 說明 |
|---|---|---|
| forEach | void | 遍歷 |
| collect | R | 收集到集合 |
| toArray | T[] | 轉為陣列 |
| reduce | T/Optional | 歸約 |
| count | long | 計數 |
| min/max | Optional | 最小/最大 |
| findFirst | Optional | 第一個 |
| findAny | Optional | 任意一個 |
| anyMatch | boolean | 任一匹配 |
| allMatch | boolean | 全部匹配 |
| noneMatch | boolean | 沒有匹配 |
| sum | int/long/double | 求和 |
| average | OptionalDouble | 平均值 |
重點整理
- 終端操作觸發 Stream 執行
collect是最常用的終端操作reduce用於將元素歸約為單一結果- 使用
Optional處理可能為空的結果 - 基本型別 Stream 有
sum()、average()等便利方法 - 短路操作可以提早結束,提高效能