Stream API 深度解析
Stream API 是 Java 8 引入的数据处理利器,让集合操作更简洁、更高效。
一、Stream 核心概念
1.1 什么是 Stream
Stream = 数据流 + 函数式操作
特点:
- 不存储数据
- 不改变源数据
- 懒加载(惰性求值)
- 可并行处理
1.2 Stream vs Collection
// Collection:存储数据
List<String> list = Arrays.asList("a", "b", "c");
// Stream:处理数据
Stream<String> stream = list.stream();
| 特性 | Collection | Stream |
|---|---|---|
| 存储 | 存储数据 | 不存储数据 |
| 修改 | 可修改 | 不可变 |
| 迭代 | 外部迭代 | 内部迭代 |
| 求值 | 立即 | 懒加载 |
二、创建 Stream
2.1 从集合创建
// Collection
List<String> list = Arrays.asList("a", "b", "c");
Stream<String> stream = list.stream();
Stream<String> parallelStream = list.parallelStream();
// Set
Set<String> set = new HashSet<>();
Stream<String> stream = set.stream();
// Map
Map<String, Integer> map = new HashMap<>();
Stream<String> keyStream = map.keySet().stream();
Stream<Integer> valueStream = map.values().stream();
Stream<Map.Entry<String, Integer>> entryStream = map.entrySet().stream();
2.2 从数组创建
String[] arr = {"a", "b", "c"};
Stream<String> stream = Arrays.stream(arr);
Stream<String> stream2 = Stream.of(arr);
2.3 生成 Stream
// 空 Stream
Stream<String> empty = Stream.empty();
// 单个值
Stream<String> single = Stream.of("a");
// 多个值
Stream<String> multi = Stream.of("a", "b", "c");
// 无限流
Stream<Double> randoms = Stream.generate(Math::random);
Stream<Integer> naturals = Stream.iterate(0, n -> n + 1);
// 有限流
Stream<Integer> evens = Stream.iterate(0, n -> n + 2).limit(10);
2.4 从文件/IO 创建
// 文件行
Stream<String> lines = Files.lines(Paths.get("file.txt"));
// 目录
Stream<Path> paths = Files.list(Paths.get("/dir"));
// BufferedReader
Stream<String> lines = bufferedReader.lines();
三、中间操作
3.1 过滤
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6);
// filter
numbers.stream()
.filter(n -> n % 2 == 0)
.forEach(System.out::println); // [2, 4, 6]
// distinct
Stream.of(1, 2, 2, 3, 3, 3)
.distinct()
.forEach(System.out::println); // [1, 2, 3]
3.2 转换
// map
List<String> list = Arrays.asList("a", "b", "c");
list.stream()
.map(String::toUpperCase)
.forEach(System.out::println); // [A, B, C]
// flatMap
List<List<String>> nested = Arrays.asList(
Arrays.asList("a", "b"),
Arrays.asList("c", "d")
);
nested.stream()
.flatMap(List::stream)
.forEach(System.out::println); // [a, b, c, d]
// mapToInt/mapToLong/mapToDouble
list.stream()
.mapToInt(String::length)
.sum();
3.3 切片
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
// limit
numbers.stream().limit(3).forEach(System.out::println); // [1, 2, 3]
// skip
numbers.stream().skip(2).forEach(System.out::println); // [3, 4, 5]
// takeWhile(Java 9)
numbers.stream()
.takeWhile(n -> n < 4)
.forEach(System.out::println); // [1, 2, 3]
// dropWhile(Java 9)
numbers.stream()
.dropWhile(n -> n < 4)
.forEach(System.out::println); // [4, 5]
3.4 排序
List<String> list = Arrays.asList("c", "a", "b");
// 自然排序
list.stream()
.sorted()
.forEach(System.out::println); // [a, b, c]
// 自定义排序
list.stream()
.sorted(Comparator.reverseOrder())
.forEach(System.out::println); // [c, b, a]
// 比较器链式
list.stream()
.sorted(Comparator.comparing(String::length)
.thenComparing(Comparator.naturalOrder()))
.forEach(System.out::println);
四、终端操作
4.1 查找
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
// anyMatch
boolean hasEven = numbers.stream().anyMatch(n -> n % 2 == 0); // true
// allMatch
boolean allPositive = numbers.stream().allMatch(n -> n > 0); // true
// noneMatch
boolean noneNegative = numbers.stream().noneMatch(n -> n < 0); // true
// findFirst
Optional<Integer> first = numbers.stream().findFirst();
// findAny(并行流更高效)
Optional<Integer> any = numbers.parallelStream().findAny();
4.2 归约
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
// reduce
Optional<Integer> sum = numbers.stream()
.reduce((a, b) -> a + b);
// 带初始值
Integer sum2 = numbers.stream()
.reduce(0, (a, b) -> a + b);
// 三元 reduce
Integer sum3 = numbers.stream()
.reduce(0,
Integer::sum, // 累加器
Integer::sum // 合并器(并行用)
);
// 最大值/最小值
Optional<Integer> max = numbers.stream()
.reduce(Integer::max);
Optional<Integer> min = numbers.stream()
.reduce(Integer::min);
4.3 收集
List<String> list = Arrays.asList("a", "b", "c");
// toList
List<String> collected = list.stream()
.map(String::toUpperCase)
.collect(Collectors.toList());
// toSet
Set<String> set = list.stream()
.collect(Collectors.toSet());
// toMap
Map<String, Integer> map = list.stream()
.collect(Collectors.toMap(
s -> s, // key
String::length // value
));
// 处理 key 冲突
Map<String, Integer> map2 = list.stream()
.collect(Collectors.toMap(
s -> s,
String::length,
(v1, v2) -> v1 + v2 // 冲突处理
));
// joining
String joined = list.stream()
.collect(Collectors.joining(", ", "[", "]"));
// [a, b, c]
4.4 统计
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
// count
long count = numbers.stream().count();
// sum
int sum = numbers.stream()
.mapToInt(Integer::intValue)
.sum();
// average
OptionalDouble avg = numbers.stream()
.mapToInt(Integer::intValue)
.average();
// 统计对象
IntSummaryStatistics stats = numbers.stream()
.mapToInt(Integer::intValue)
.summaryStatistics();
System.out.println(stats.getCount()); // 5
System.out.println(stats.getSum()); // 15
System.out.println(stats.getAverage()); // 3.0
System.out.println(stats.getMin()); // 1
System.out.println(stats.getMax()); // 5
五、Collectors 详解
5.1 分组
List<Person> people = Arrays.asList(
new Person("Alice", 20),
new Person("Bob", 25),
new Person("Charlie", 20)
);
// 单级分组
Map<Integer, List<Person>> byAge = people.stream()
.collect(Collectors.groupingBy(Person::getAge));
// 多级分组
Map<Integer, Map<Character, List<Person>>> grouped = people.stream()
.collect(Collectors.groupingBy(
Person::getAge,
Collectors.groupingBy(p -> p.getName().charAt(0))
));
// 分组后统计
Map<Integer, Long> countByAge = people.stream()
.collect(Collectors.groupingBy(
Person::getAge,
Collectors.counting()
));
5.2 分区
// 分区(true/false)
Map<Boolean, List<Person>> partitioned = people.stream()
.collect(Collectors.partitioningBy(p -> p.getAge() > 20));
// 分区后统计
Map<Boolean, Long> partitionCount = people.stream()
.collect(Collectors.partitioningBy(
p -> p.getAge() > 20,
Collectors.counting()
));
5.3 下游收集器
// 分组后连接
Map<Integer, String> namesByAge = people.stream()
.collect(Collectors.groupingBy(
Person::getAge,
Collectors.mapping(Person::getName, Collectors.joining(", "))
));
// {20=Alice, Charlie, 25=Bob}
// 分组后求平均
Map<Integer, Double> avgAge = people.stream()
.collect(Collectors.groupingBy(
Person::getAge,
Collectors.averagingInt(Person::getAge)
));
六、并行流
6.1 创建并行流
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
// 方式 1:parallelStream
numbers.parallelStream()
.forEach(System.out::println);
// 方式 2:parallel
numbers.stream()
.parallel()
.forEach(System.out::println);
// 方式 3:并行流转串行
numbers.parallelStream()
.sequential()
.forEach(System.out::println);
6.2 性能优化
// 适合并行处理的场景
// 1. 数据量大
// 2. CPU 密集型操作
// 3. 无序流
List<Long> numbers = LongStream.rangeClosed(1, 1_000_000_000L)
.boxed()
.collect(Collectors.toList());
// 串行
long start = System.currentTimeMillis();
numbers.stream()
.mapToLong(Long::longValue)
.sum();
System.out.println("Sequential: " + (System.currentTimeMillis() - start) + "ms");
// 并行
start = System.currentTimeMillis();
numbers.parallelStream()
.mapToLong(Long::longValue)
.sum();
System.out.println("Parallel: " + (System.currentTimeMillis() - start) + "ms");
6.3 注意事项
// ❌ 避免:有状态操作
numbers.parallelStream()
.map(n -> {
// 依赖外部状态
return n + counter++;
});
// ✅ 正确:无状态操作
numbers.parallelStream()
.map(n -> n * 2);
// ❌ 避免:顺序敏感操作
numbers.parallelStream()
.forEach(System.out::println); // 顺序不确定
// ✅ 正确:保证顺序
numbers.parallelStream()
.forEachOrdered(System.out::println);
七、最佳实践
7.1 选择合适操作
// ❌ 低效
list.stream()
.filter(s -> s != null)
.map(String::toUpperCase)
.collect(Collectors.toList());
// ✅ 高效
list.stream()
.filter(Objects::nonNull)
.map(String::toUpperCase)
.collect(Collectors.toList());
7.2 避免过早优化
// ❌ 过度使用并行流
list.parallelStream().map(...);
// ✅ 先串行,性能不足再并行
list.stream().map(...);
7.3 使用 Stream 简化代码
// 之前
List<String> result = new ArrayList<>();
for (String s : list) {
if (s.startsWith("A")) {
result.add(s.toUpperCase());
}
}
// Stream
List<String> result = list.stream()
.filter(s -> s.startsWith("A"))
.map(String::toUpperCase)
.collect(Collectors.toList());
八、总结
Stream API 核心要点:
| 操作类型 | 常用方法 |
|---|---|
| 中间操作 | filter, map, flatMap, sorted, limit, skip |
| 终端操作 | forEach, collect, reduce, count, sum |
| 收集器 | toList, toMap, groupingBy, joining |
| 并行流 | parallelStream, sequential |
Stream 让代码更简洁,但要注意性能,避免滥用并行流。