1. Stream API 简介
Stream API 是 Java 8 引入的革命性特性,它提供了一种高效且易于使用的方式来处理数据集合。 Stream 不是集合元素,它不是数据结构并不保存数据,而是关于算法和计算的,它更像是一个高级版本的 Iterator。
Stream API 的核心优势在于:
- 代码简洁:使用声明式编程,代码更加清晰易读
- 性能优化:支持并行处理,充分利用多核 CPU
- 函数式风格:支持函数式编程范式,减少副作用
- 延迟执行:中间操作延迟执行,提高效率
2. Stream 的基本操作
Stream 操作分为两类:中间操作和终端操作。
2.1 创建 Stream
// 从集合创建 Stream
List list = Arrays.asList("a", "b", "c");
Stream stream = list.stream();
// 从数组创建 Stream
String[] array = {"a", "b", "c"};
Stream stream = Arrays.stream(array);
// 使用 Stream.of() 创建
Stream stream = Stream.of("a", "b", "c");
// 创建无限 Stream
Stream iterate = Stream.iterate(0, n -> n + 2);
Stream generate = Stream.generate(Math::random);
2.2 常用中间操作
// filter:过滤元素
List numbers = Arrays.asList(1, 2, 3, 4, 5);
List evenNumbers = numbers.stream()
.filter(n -> n % 2 == 0)
.collect(Collectors.toList());
// map:转换元素
List names = Arrays.asList("Alice", "Bob", "Charlie");
List nameLengths = names.stream()
.map(String::length)
.collect(Collectors.toList());
// flatMap:扁平化处理
List> nestedLists = Arrays.asList(
Arrays.asList(1, 2),
Arrays.asList(3, 4),
Arrays.asList(5, 6)
);
List flattened = nestedLists.stream()
.flatMap(Collection::stream)
.collect(Collectors.toList());
// sorted:排序
List sortedNumbers = numbers.stream()
.sorted()
.collect(Collectors.toList());
// distinct:去重
List distinctNumbers = numbers.stream()
.distinct()
.collect(Collectors.toList());
2.3 常用终端操作
// collect:收集结果
List result = names.stream()
.filter(name -> name.length() > 3)
.collect(Collectors.toList());
// forEach:遍历元素
names.stream().forEach(System.out::println);
// reduce:聚合操作
Integer sum = numbers.stream()
.reduce(0, Integer::sum);
// count:计数
long count = names.stream()
.filter(name -> name.startsWith("A"))
.count();
// min/max:最小/最大值
Optional min = numbers.stream().min(Integer::compare);
Optional max = numbers.stream().max(Integer::compare);
3. Stream 性能优化
Stream API 的性能优化是实际项目中的关键考虑因素。以下是几个重要的优化技巧:
3.1 使用并行流
// 串行流
long serialTime = measureTime(() ->
numbers.stream()
.map(n -> n * n)
.collect(Collectors.toList())
);
// 并行流
long parallelTime = measureTime(() ->
numbers.parallelStream()
.map(n -> n * n)
.collect(Collectors.toList())
);
System.out.println("串行耗时: " + serialTime + "ms");
System.out.println("并行耗时: " + parallelTime + "ms");
注意:并行流并不总是比串行流快。对于小数据集或简单操作,并行流的线程切换开销可能超过收益。
3.2 避免不必要的装箱/拆箱
// 使用原始类型 Stream(IntStream, LongStream, DoubleStream)
int sum = IntStream.range(1, 100)
.sum(); // 避免 Integer 拆箱
// 替代方案(效率较低)
Integer sum2 = Stream.iterate(1, n -> n + 1)
.limit(99)
.reduce(0, Integer::sum);
3.3 合理使用短路操作
// findFirst/findAny:找到第一个/任意一个元素
Optional firstMatch = names.stream()
.filter(name -> name.startsWith("A"))
.findFirst(); // 找到后立即停止
// anyMatch/allMatch/noneMatch:判断是否存在匹配
boolean anyMatch = names.stream()
.anyMatch(name -> name.length() > 10); // 找到匹配立即返回
4. 实战应用场景
4.1 数据转换与过滤
// 实际业务场景:从用户列表中筛选活跃用户
public List getActiveUsers(List users) {
return users.stream()
.filter(user -> user.getLastLoginTime() != null)
.filter(user -> user.getLastLoginTime().isAfter(
LocalDateTime.now().minusDays(30)))
.sorted(Comparator.comparing(User::getLastLoginTime).reversed())
.limit(100)
.collect(Collectors.toList());
}
4.2 分组与统计
// 按部门分组统计员工数量
Map departmentCount = employees.stream()
.collect(Collectors.groupingBy(
Employee::getDepartment,
Collectors.counting()
));
// 按部门分组并计算平均薪资
Map avgSalaryByDept = employees.stream()
.collect(Collectors.groupingBy(
Employee::getDepartment,
Collectors.averagingDouble(Employee::getSalary)
));
4.3 复合操作
// 综合示例:处理订单数据
public OrderSummary summarizeOrders(List orders) {
return new OrderSummary(
orders.stream().mapToDouble(Order::getAmount).sum(), // 总金额
orders.stream().mapToDouble(Order::getAmount).average().orElse(0), // 平均金额
orders.stream().count(), // 订单数量
orders.stream()
.collect(Collectors.groupingBy(Order::getStatus, Collectors.counting())) // 各状态数量
);
}
5. Stream API 最佳实践
5.1 代码可读性
- 链式调用控制在3-5行,过长会降低可读性
- 复杂逻辑拆分为多个中间变量
- 使用方法引用代替 Lambda 表达式(当语义清晰时)
// 好的示例:简洁清晰
List activeUserNames = users.stream()
.filter(User::isActive)
.map(User::getName)
.collect(Collectors.toList());
// 不好的示例:过度复杂
List result = users.stream()
.filter(user -> user.isActive() && user.getAge() > 18 && user.getDepartment().equals("IT"))
.map(user -> user.getName().toUpperCase().trim())
.sorted(Comparator.comparing(String::length).reversed())
.distinct()
.limit(10)
.skip(2)
.collect(Collectors.toList());
5.2 异常处理
Stream 操作中的异常处理需要特别注意:
// 使用 try-catch 包装 Lambda
public List safeProcess(List data) {
return data.stream()
.map(item -> {
try {
return processItem(item);
} catch (Exception e) {
log.error("处理失败: " + item, e);
return null; // 或使用 Optional.empty()
}
})
.filter(Objects::nonNull)
.collect(Collectors.toList());
}
5.3 资源管理
对于需要关闭的资源(如文件流),使用 try-with-resources:
// 读取文件并处理
try (Stream lines = Files.lines(Paths.get("data.txt"))) {
List filteredLines = lines
.filter(line -> line.contains("keyword"))
.collect(Collectors.toList());
} catch (IOException e) {
log.error("文件读取失败", e);
}
6. 总结
Stream API 是 Java 8 最重要的特性之一,它彻底改变了我们处理集合数据的方式。 通过本文的学习,你应该掌握了:
- Stream API 的核心概念和操作分类
- 常用中间操作和终端操作的使用方法
- 性能优化的关键技巧(并行流、短路操作等)
- 实际业务场景中的应用示例
- 代码可读性和异常处理的最佳实践
在实际开发中,合理使用 Stream API 可以大幅提升代码质量和开发效率。 但需要注意,过度使用或不当使用反而会降低代码可读性和性能。
最佳实践原则:简洁优于复杂,可读性优于炫技,性能测试优于主观猜测。