Java Stream API详解

Java 8引入的Stream API彻底改变了集合数据处理的写法,用声明式的链式调用替代了以前的for循环。代码更简洁、可读性更好,还能方便地切换到并行流。这篇系统整理Stream的常用操作。

创建Stream

import java.util.*;
import java.util.stream.*;

// 从集合创建
List<String> list = Arrays.asList("a", "b", "c");
Stream<String> stream = list.stream();

// 从数组创建
int[] arr = {1, 2, 3, 4, 5};
IntStream intStream = Arrays.stream(arr);

// Stream.of
Stream<String> s = Stream.of("x", "y", "z");

// 生成无限流
Stream<Integer> randoms = Stream.generate(() -> new Random().nextInt(100));
Stream<Integer> naturals = Stream.iterate(0, n -> n + 1);

中间操作

中间操作返回新的Stream,可以链式调用,且是惰性求值(只有遇到终止操作才真正执行)。

filter

List<Integer> nums = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8);

List<Integer> evens = nums.stream()
    .filter(n -> n % 2 == 0)
    .collect(Collectors.toList());
// [2, 4, 6, 8]

map

List<String> names = Arrays.asList("alice", "bob", "charlie");

List<String> upperNames = names.stream()
    .map(String::toUpperCase)
    .collect(Collectors.toList());
// [ALICE, BOB, CHARLIE]

// mapToInt / mapToLong / mapToDouble 避免装箱
int totalLength = names.stream()
    .mapToInt(String::length)
    .sum();

flatMap

把嵌套结构展平:

List<List<Integer>> nested = Arrays.asList(
    Arrays.asList(1, 2, 3),
    Arrays.asList(4, 5),
    Arrays.asList(6, 7, 8, 9)
);

List<Integer> flat = nested.stream()
    .flatMap(Collection::stream)
    .collect(Collectors.toList());
// [1, 2, 3, 4, 5, 6, 7, 8, 9]

sorted

List<String> sorted = names.stream()
    .sorted()  // 自然排序
    .collect(Collectors.toList());

// 自定义排序
List<String> sortedByLength = names.stream()
    .sorted(Comparator.comparingInt(String::length).reversed())
    .collect(Collectors.toList());
// [charlie, alice, bob]

distinct / limit / skip / peek

// 去重
List<Integer> unique = Arrays.asList(1, 2, 2, 3, 3, 3).stream()
    .distinct()
    .collect(Collectors.toList());  // [1, 2, 3]

// 取前N个
List<Integer> firstThree = nums.stream()
    .limit(3)
    .collect(Collectors.toList());  // [1, 2, 3]

// 跳过前N个
List<Integer> afterTwo = nums.stream()
    .skip(2)
    .collect(Collectors.toList());  // [3, 4, 5, 6, 7, 8]

// peek用于调试(不影响流)
nums.stream()
    .filter(n -> n > 3)
    .peek(n -> System.out.println("filtered: " + n))
    .map(n -> n * 2)
    .collect(Collectors.toList());

终止操作

collect

最常用的终止操作,配合Collectors工具类:

// 转List
List<String> list = stream.collect(Collectors.toList());

// 转Set
Set<String> set = stream.collect(Collectors.toSet());

// 转Map
Map<Integer, String> map = names.stream()
    .collect(Collectors.toMap(String::length, s -> s, (a, b) -> a));

// joining
String joined = names.stream()
    .collect(Collectors.joining(", ", "[", "]"));
// [alice, bob, charlie]

// 分组
Map<Integer, List<String>> grouped = names.stream()
    .collect(Collectors.groupingBy(String::length));
// {3=[bob], 5=[alice], 7=[charlie]}

// 分区(按条件分成两组)
Map<Boolean, List<Integer>> partitioned = nums.stream()
    .collect(Collectors.partitioningBy(n -> n > 5));

forEach

names.stream().forEach(System.out::println);

// 注意:forEach是终止操作,执行后Stream就消费完了

reduce

// 求和
Optional<Integer> sum = nums.stream()
    .reduce(Integer::sum);

// 带初始值
int total = nums.stream()
    .reduce(0, Integer::sum);

// 求最大值
Optional<Integer> max = nums.stream()
    .reduce(Integer::max);

其他终止操作

long count = stream.count();
boolean anyMatch = stream.anyMatch(s -> s.startsWith("a"));
boolean allMatch = stream.allMatch(s -> s.length() > 2);
Optional<String> first = stream.findFirst();
Optional<String> any = stream.findAny();  // 并行流中有用

并行流

// 直接创建并行流
list.parallelStream()
    .filter(s -> s.length() > 3)
    .collect(Collectors.toList());

// 普通流转并行流
list.stream()
    .parallel()
    .map(String::toUpperCase)
    .collect(Collectors.toList());

并行流底层用ForkJoinPool,适合数据量大、无状态的操作。注意事项:

  • 数据量小时并行可能更慢(线程调度开销)
  • 不要在并行流中修改共享状态
  • 有序性要求高的场景慎用

实际例子

统计一段文本中每个单词的出现次数:

String text = "hello world hello java world hello stream";

Map<String, Long> wordCount = Arrays.stream(text.split("\\s+"))
    .collect(Collectors.groupingBy(w -> w, Collectors.counting()));
// {hello=3, world=2, java=1, stream=1}

从用户列表中找出活跃用户的邮箱:

List<String> activeEmails = users.stream()
    .filter(User::isActive)
    .filter(u -> u.getAge() >= 18)
    .sorted(Comparator.comparing(User::getName))
    .map(User::getEmail)
    .distinct()
    .collect(Collectors.toList());

小结

Stream API的核心模式就是:创建流 -> 中间操作(filter/map/sorted等)-> 终止操作(collect/forEach/neduce等)。习惯了之后,集合处理的代码量会少很多,而且表达力更强。建议多用,但不要为了用Stream而用——简单的循环就能搞定的,没必要硬凹成Stream写法。