Java Stream API
Stream API 是 Java 8 引入的功能,提供函數式的方式處理集合資料。
建立 Stream
// 從集合
List<String> list = Arrays.asList("A", "B", "C");
Stream<String> stream1 = list.stream();
// 從陣列
String[] arr = {"A", "B", "C"};
Stream<String> stream2 = Arrays.stream(arr);
// 使用 Stream.of
Stream<String> stream3 = Stream.of("A", "B", "C");
// 產生
Stream<Integer> stream4 = Stream.iterate(0, n -> n + 1).limit(10);
Stream<Double> stream5 = Stream.generate(Math::random).limit(5);
// IntStream, LongStream, DoubleStream
IntStream intStream = IntStream.range(1, 10); // 1 到 9
IntStream intStream2 = IntStream.rangeClosed(1, 10); // 1 到 10
更多說明請參考 Java Stream 建立。
中間操作
中間操作回傳 Stream,可以鏈式呼叫:
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
// filter:過濾
numbers.stream()
.filter(n -> n % 2 == 0) // 偶數
.forEach(System.out::println); // 2, 4, 6, 8, 10
// map:轉換
numbers.stream()
.map(n -> n * 2) // 乘以 2
.forEach(System.out::println);
// sorted:排序
numbers.stream()
.sorted(Comparator.reverseOrder()) // 降冪
.forEach(System.out::println);
// distinct:去重
Stream.of(1, 2, 2, 3, 3, 3)
.distinct()
.forEach(System.out::println); // 1, 2, 3
// limit:限制數量
numbers.stream()
.limit(3)
.forEach(System.out::println); // 1, 2, 3
// skip:跳過
numbers.stream()
.skip(5)
.forEach(System.out::println); // 6, 7, 8, 9, 10
更多說明請參考 Java Stream 中間操作。
終端操作
終端操作會觸發 Stream 執行並回傳結果:
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
// forEach:遍歷
numbers.stream().forEach(System.out::println);
// collect:收集結果
List<Integer> result = numbers.stream()
.filter(n -> n > 2)
.collect(Collectors.toList());
// reduce:聚合
int sum = numbers.stream()
.reduce(0, Integer::sum); // 15
// count:計數
long count = numbers.stream()
.filter(n -> n > 2)
.count(); // 3
// findFirst / findAny
Optional<Integer> first = numbers.stream()
.filter(n -> n > 2)
.findFirst(); // Optional[3]
// anyMatch / allMatch / noneMatch
boolean anyEven = numbers.stream().anyMatch(n -> n % 2 == 0); // true
boolean allPositive = numbers.stream().allMatch(n -> n > 0); // true
更多說明請參考 Java Stream 終端操作。
Collectors
常用的收集器:
List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "Alice");
// toList
List<String> list = names.stream().collect(Collectors.toList());
// toSet
Set<String> set = names.stream().collect(Collectors.toSet());
// joining
String joined = names.stream().collect(Collectors.joining(", "));
// "Alice, Bob, Charlie, Alice"
// groupingBy
Map<Integer, List<String>> byLength = names.stream()
.collect(Collectors.groupingBy(String::length));
// {3=[Bob], 5=[Alice, Alice], 7=[Charlie]}
// counting
Map<String, Long> counting = names.stream()
.collect(Collectors.groupingBy(s -> s, Collectors.counting()));
// {Alice=2, Bob=1, Charlie=1}
// summarizing
IntSummaryStatistics stats = names.stream()
.collect(Collectors.summarizingInt(String::length));
// count=4, sum=20, min=3, average=5.0, max=7
更多說明請參考 Java Collectors。
實際應用
過濾和轉換
List<Person> people = getPeople();
// 取得所有成年人的名字
List<String> adultNames = people.stream()
.filter(p -> p.getAge() >= 18)
.map(Person::getName)
.collect(Collectors.toList());
統計
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
int sum = numbers.stream().mapToInt(Integer::intValue).sum();
double avg = numbers.stream().mapToInt(Integer::intValue).average().orElse(0);
int max = numbers.stream().mapToInt(Integer::intValue).max().orElse(0);
int min = numbers.stream().mapToInt(Integer::intValue).min().orElse(0);
分組
List<Person> people = getPeople();
// 按城市分組
Map<String, List<Person>> byCity = people.stream()
.collect(Collectors.groupingBy(Person::getCity));
// 按年齡分組並計數
Map<Integer, Long> countByAge = people.stream()
.collect(Collectors.groupingBy(Person::getAge, Collectors.counting()));
flatMap
處理巢狀結構:
List<List<Integer>> nested = Arrays.asList(
Arrays.asList(1, 2, 3),
Arrays.asList(4, 5, 6),
Arrays.asList(7, 8, 9)
);
// 攤平成一維
List<Integer> flat = nested.stream()
.flatMap(List::stream)
.collect(Collectors.toList());
// [1, 2, 3, 4, 5, 6, 7, 8, 9]
平行 Stream
使用多執行緒處理:
List<Integer> numbers = IntStream.rangeClosed(1, 1000000)
.boxed()
.collect(Collectors.toList());
// 平行處理
long count = numbers.parallelStream()
.filter(n -> n % 2 == 0)
.count();
更多說明請參考 Java 平行 Stream。
注意事項
- Stream 只能使用一次
- 惰性求值:中間操作不會立即執行
- 副作用:避免在 Lambda 中修改外部狀態
- 平行處理:注意執行緒安全