目录

Java Stream API 详解

掌握 Stream 函数式编程,提升集合数据处理效率

什么是 Stream

Stream 是 Java 8 引入的函数式编程特性,用于对集合数据进行高效、声明式的操作。

特点:

  • 链式操作,代码简洁
  • 支持并行处理
  • 惰性求值,性能优化
  • 不修改原数据源

创建 Stream

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

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

// 2. 从数组创建
Stream<String> stream2 = Arrays.stream(new String[]{"a", "b", "c"});

// 3. 使用 Stream.of
Stream<String> stream3 = Stream.of("a", "b", "c");

// 4. 无限流
Stream<Integer> infiniteStream = Stream.iterate(0, n -> n + 2).limit(10);
Stream<Double> randomStream = Stream.generate(Math::random).limit(5);

// 5. 从文件创建(IO流)
Stream<String> lines = Files.lines(Paths.get("file.txt"));

// 6. 空流
Stream<String> emptyStream = Stream.empty();

中间操作(Intermediate Operations)

中间操作返回新的 Stream,可以链式调用。

filter - 过滤

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

// 过滤偶数
List<Integer> evens = numbers.stream()
    .filter(n -> n % 2 == 0)
    .collect(Collectors.toList());
// [2, 4, 6]

// 过滤非空字符串
List<String> nonEmpty = strings.stream()
    .filter(s -> s != null && !s.isEmpty())
    .collect(Collectors.toList());

map - 映射转换

// 转换为大写
List<String> upperCase = names.stream()
    .map(String::toUpperCase)
    .collect(Collectors.toList());

// 提取属性
List<Integer> lengths = names.stream()
    .map(String::length)
    .collect(Collectors.toList());

// 对象转换
List<String> userNames = users.stream()
    .map(User::getName)
    .collect(Collectors.toList());

// flatMap - 扁平化
List<List<Integer>> nested = Arrays.asList(
    Arrays.asList(1, 2),
    Arrays.asList(3, 4),
    Arrays.asList(5, 6)
);
List<Integer> flat = nested.stream()
    .flatMap(List::stream)
    .collect(Collectors.toList());
// [1, 2, 3, 4, 5, 6]

sorted - 排序

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

// 自定义排序
List<User> sortedByAge = users.stream()
    .sorted(Comparator.comparing(User::getAge))
    .collect(Collectors.toList());

// 倒序
List<User> sortedDesc = users.stream()
    .sorted(Comparator.comparing(User::getAge).reversed())
    .collect(Collectors.toList());

// 多级排序
List<User> sortedMulti = users.stream()
    .sorted(Comparator.comparing(User::getAge)
        .thenComparing(User::getName))
    .collect(Collectors.toList());

distinct - 去重

List<Integer> unique = numbers.stream()
    .distinct()
    .collect(Collectors.toList());

// 根据属性去重(需要自定义)
List<User> uniqueUsers = users.stream()
    .collect(Collectors.collectingAndThen(
        Collectors.toCollection(() -> 
            new TreeSet<>(Comparator.comparing(User::getId))),
        ArrayList::new
    ));

limit 和 skip - 分页

List<Integer> limited = numbers.stream()
    .limit(3)  // 取前3个
    .collect(Collectors.toList());

List<Integer> skipped = numbers.stream()
    .skip(3)   // 跳过前3个
    .collect(Collectors.toList());

// 分页实现
int page = 1;
int size = 10;
List<User> pageData = users.stream()
    .skip((long) (page - 1) * size)
    .limit(size)
    .collect(Collectors.toList());

peek - 调试

List<Integer> result = numbers.stream()
    .peek(n -> System.out.println("处理前: " + n))
    .map(n -> n * 2)
    .peek(n -> System.out.println("处理后: " + n))
    .collect(Collectors.toList());

终止操作(Terminal Operations)

forEach - 遍历

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

// 带索引的遍历
AtomicInteger index = new AtomicInteger(0);
names.stream()
    .forEach(name -> System.out.println(index.getAndIncrement() + ": " + name));

collect - 收集

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

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

// 转为特定集合
LinkedList<String> linkedList = stream.collect(
    Collectors.toCollection(LinkedList::new));

// 转为 Map
Map<Long, String> idNameMap = users.stream()
    .collect(Collectors.toMap(User::getId, User::getName));

// 处理键冲突
Map<String, Integer> nameAgeMap = users.stream()
    .collect(Collectors.toMap(
        User::getName,
        User::getAge,
        (existing, replacement) -> existing  // 保留旧值
    ));

// 分组
Map<String, List<User>> groupByCity = users.stream()
    .collect(Collectors.groupingBy(User::getCity));

// 多级分组
Map<String, Map<String, List<User>>> groupByCityAndGender = users.stream()
    .collect(Collectors.groupingBy(
        User::getCity,
        Collectors.groupingBy(User::getGender)
    ));

// 分区(二分组)
Map<Boolean, List<User>> partitionByAge = users.stream()
    .collect(Collectors.partitioningBy(u -> u.getAge() >= 18));

// 统计
IntSummaryStatistics stats = numbers.stream()
    .collect(Collectors.summarizingInt(Integer::intValue));
System.out.println(stats.getAverage());  // 平均数
System.out.println(stats.getSum());      // 总和
System.out.println(stats.getMax());      // 最大值
System.out.println(stats.getMin());      // 最小值

// 连接字符串
String joined = names.stream()
    .collect(Collectors.joining(", ", "[", "]"));
// [Alice, Bob, Charlie]

// 自定义收集器
String result = stream.collect(
    StringBuilder::new,
    StringBuilder::append,
    StringBuilder::append
).toString();

reduce - 归约

// 求和
int sum = numbers.stream()
    .reduce(0, Integer::sum);

// 阶乘
OptionalInt factorial = IntStream.rangeClosed(1, 5)
    .reduce((a, b) -> a * b);
// 120

// 字符串连接
String concatenated = names.stream()
    .reduce("", (a, b) -> a + " " + b);

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

匹配与查找

// anyMatch - 是否包含
boolean hasEven = numbers.stream()
    .anyMatch(n -> n % 2 == 0);

// allMatch - 是否全部满足
boolean allPositive = numbers.stream()
    .allMatch(n -> n > 0);

// noneMatch - 是否全部不满足
boolean noNegative = numbers.stream()
    .noneMatch(n -> n < 0);

// findFirst - 查找第一个
Optional<String> first = names.stream()
    .filter(s -> s.startsWith("A"))
    .findFirst();

// findAny - 查找任意一个(并行流更高效)
Optional<String> any = names.parallelStream()
    .filter(s -> s.length() > 3)
    .findAny();

统计

long count = stream.count();

Optional<T> max = stream.max(Comparator.naturalOrder());
Optional<T> min = stream.min(Comparator.naturalOrder());

数值流

// IntStream
int sum = IntStream.of(1, 2, 3, 4, 5).sum();
double average = IntStream.range(1, 100).average().orElse(0);
IntSummaryStatistics stats = IntStream.of(1, 2, 3).summaryStatistics();

// 对象流转数值流
int totalAge = users.stream()
    .mapToInt(User::getAge)
    .sum();

// 数值流转对象流
Stream<Integer> boxedStream = IntStream.of(1, 2, 3).boxed();

并行流

// 创建并行流
List<Integer> result = numbers.parallelStream()
    .map(n -> heavyComputation(n))
    .collect(Collectors.toList());

// 普通流转并行流
List<Integer> result = numbers.stream()
    .parallel()
    .map(n -> heavyComputation(n))
    .collect(Collectors.toList());

// 适合并行流的场景:
// 1. 数据量大(通常 > 10,000)
// 2. 复杂计算
// 3. 无状态无副作用

实际应用示例

示例 1:数据处理

// 获取年龄大于18岁的用户名,按年龄排序
List<String> result = users.stream()
    .filter(u -> u.getAge() > 18)
    .sorted(Comparator.comparing(User::getAge))
    .map(User::getName)
    .distinct()
    .limit(10)
    .collect(Collectors.toList());

示例 2:分组统计

// 按部门统计员工平均工资
Map<String, Double> avgSalaryByDept = employees.stream()
    .collect(Collectors.groupingBy(
        Employee::getDepartment,
        Collectors.averagingDouble(Employee::getSalary)
    ));

示例 3:文件处理

// 读取文件,统计单词频率
Map<String, Long> wordCount = Files.lines(Paths.get("file.txt"))
    .flatMap(line -> Arrays.stream(line.split("\\s+")))
    .map(String::toLowerCase)
    .filter(word -> !word.isEmpty())
    .collect(Collectors.groupingBy(
        Function.identity(),
        Collectors.counting()
    ));

示例 4:Optional 链式操作

// 获取第一个成年用户的名字
String name = users.stream()
    .filter(u -> u.getAge() >= 18)
    .findFirst()
    .map(User::getName)
    .orElse("Unknown");

注意事项

  1. Stream 只能消费一次
Stream<String> stream = list.stream();
stream.forEach(System.out::println);
stream.forEach(System.out::println);  // 报错
  1. 避免在 Stream 中修改外部变量
// 错误
List<Integer> result = new ArrayList<>();
numbers.forEach(result::add);

// 正确
List<Integer> result = numbers.stream()
    .collect(Collectors.toList());
  1. 谨慎使用并行流
  • 数据量小反而更慢
  • 涉及 I/O 可能阻塞 ForkJoinPool
  • 注意线程安全问题

总结

Stream API 是 Java 函数式编程的核心特性,掌握它可以写出更简洁、高效的代码。关键点:

  • 区分中间操作和终止操作
  • 熟悉常用收集器
  • 合理使用并行流
  • 注意流的一次性消费特性