Stream流式编程的最佳实践
Java 8 Stream API 提供了函数式风格的集合操作方式。本文从基础到高级,全面讲解 Stream 的正确用法和优化技巧。
Stream 核心概念
Stream 是对集合对象功能的增强,专注于对集合对象进行各种便利、高效的聚合操作。
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6);
List<Integer> result = numbers.stream() .filter(n -> n % 2 == 0) .map(n -> n * n) .collect(Collectors.toList());
|
Stream 操作分类
返回新的 Stream,惰性执行:
| 操作 |
说明 |
| filter |
过滤元素 |
| map |
一对一映射 |
| flatMap |
扁平化映射 |
| distinct |
去重 |
| sorted |
排序 |
| peek |
查看元素(调试) |
| limit |
截断 |
| skip |
跳过 |
终结操作(Terminal)
触发实际计算:
| 操作 |
说明 |
| forEach |
遍历消费 |
| collect |
收集到集合 |
| reduce |
归约计算 |
| count |
计数 |
| anyMatch/allMatch/noneMatch |
匹配判断 |
| findFirst/findAny |
查找元素 |
| min/max |
最值 |
创建 Stream 的多种方式
List<String> list = Arrays.asList("a", "b", "c"); Stream<String> stream1 = list.stream(); Stream<String> stream2 = list.parallelStream();
Stream<String> stream3 = Arrays.stream(new String[]{"a", "b"});
Stream<String> stream4 = Stream.of("a", "b", "c");
Stream<Integer> infinite = Stream.iterate(0, n -> n + 2); Stream<Integer> limited = Stream.iterate(0, n -> n + 2).limit(100);
Stream<Double> random = Stream.generate(Math::random).limit(10);
Stream<String> empty = Stream.empty();
Stream<String> concat = Stream.concat(stream1, stream2);
|
常用操作详解
map vs flatMap
List<String> upper = names.stream() .map(String::toUpperCase) .collect(Collectors.toList());
List<List<Integer>> nested = Arrays.asList( Arrays.asList(1, 2), Arrays.asList(3, 4) );
List<Integer> flat = nested.stream() .flatMap(List::stream) .collect(Collectors.toList());
|
reduce 归约
int sum = numbers.stream() .reduce(0, Integer::sum);
String joined = names.stream() .reduce("", (a, b) -> a + "," + b);
Optional<Integer> max = numbers.stream() .reduce(Integer::max);
|
collect 收集器
List<String> list = stream.collect(Collectors.toList());
Set<String> set = stream.collect(Collectors.toSet());
Map<Long, String> map = users.stream() .collect(Collectors.toMap(User::getId, User::getName));
Map<String, List<User>> group = users.stream() .collect(Collectors.groupingBy(User::getDepartment));
Map<Boolean, List<Integer>> partition = numbers.stream() .collect(Collectors.partitioningBy(n -> n % 2 == 0));
IntSummaryStatistics stats = numbers.stream() .collect(Collectors.summarizingInt(Integer::intValue));
String joined = names.stream() .collect(Collectors.joining(", ", "[", "]"));
|
并行流注意事项
List<Integer> numbers = new ArrayList<>();
numbers.parallelStream() .map(n -> heavyComputation(n)) .collect(Collectors.toList());
|
适合并行流的场景
- 数据量大(通常 > 10,000 条)
- 计算密集型操作
- 无状态、无副作用
不适合并行流的场景
- 数据量小(线程切换开销 > 并行收益)
- 需要严格顺序保证(
forEachOrdered 性能差)
- 涉及共享可变状态
List<Integer> result = new ArrayList<>(); numbers.parallelStream().forEach(result::add);
List<Integer> result = numbers.parallelStream() .collect(Collectors.toList());
|
性能优化技巧
1. 优先使用基本类型 Stream
Stream<Integer> boxed = list.stream(); int sum1 = boxed.reduce(0, Integer::sum);
IntStream primitive = list.stream().mapToInt(Integer::intValue); int sum2 = primitive.sum();
|
2. 短路操作提前返回
Optional<String> first = list.stream() .filter(s -> s.startsWith("A")) .findFirst();
|
3. 避免重复遍历
long count = list.stream().filter(...).count(); List<String> result = list.stream().filter(...).collect(...);
List<String> result = list.stream().filter(...).collect(...); long count = result.size();
|
常见陷阱
1. Stream 只能消费一次
Stream<String> stream = list.stream(); stream.forEach(System.out::println); stream.filter(s -> s.length() > 3);
|
2. 不要在 Stream 中修改源数据
List<String> list = new ArrayList<>(); list.stream().forEach(list::remove);
list.removeIf(s -> s.length() < 3);
|
3. 注意空值处理
List<String> result = list.stream() .map(s -> s.toUpperCase()) .collect(Collectors.toList());
List<String> result = list.stream() .filter(Objects::nonNull) .map(String::toUpperCase) .collect(Collectors.toList());
|
最佳实践总结
- 优先使用 Stream:替代传统 for 循环,代码更简洁
- 方法引用优于 Lambda:可读性更好
- 注意并行流的前提条件:数据量大 + 无共享状态
- 不要滥用 Stream:简单场景直接 for-each
- 避免在 Stream 中修改外部状态:保持函数式纯净
Map<String, List<Employee>> byDept = employees.stream() .filter(e -> e.getSalary() > 50000) .sorted(Comparator.comparing(Employee::getName)) .collect(Collectors.groupingBy(Employee::getDepartment));
|
掌握 Stream API 是现代 Java 开发的必备技能,它能显著提升代码的简洁性和可读性。