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

注意事項

  1. Stream 只能使用一次
  2. 惰性求值:中間操作不會立即執行
  3. 副作用:避免在 Lambda 中修改外部狀態
  4. 平行處理:注意執行緒安全