Java 8 引入的 Stream 是函数式编程的重要体现,能极大简化集合操作的复杂度。本文从基础用法到进阶技巧,全面梳理 Java Stream 的常见应用场景。
一、什么是 Stream?
Stream 是对集合(Collection)对象功能的增强,它不是数据结构,不会存储数据,而是以声明式方式对数据进行处理(类似 SQL 风格)。
Stream 特点:
- 不修改原数据结构
- 惰性求值(lazy evaluation)
- 支持链式调用
- 天然支持并行操作(parallelStream)
二、Stream 创建方式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| List<String> list = Arrays.asList("apple", "banana", "orange");
Stream<String> stream1 = list.stream();
Stream<Integer> stream2 = Stream.of(1, 2, 3, 4);
Stream<Double> stream3 = Stream.generate(Math::random).limit(10); ````
---
## 三、常用操作(中间操作)
### 1. filter(过滤)
```java list.stream() .filter(s -> s.startsWith("a")) .forEach(System.out::println);
|
2. map(映射)
1 2 3
| List<Integer> lengths = list.stream() .map(String::length) .collect(Collectors.toList());
|
3. sorted(排序)
1 2 3
| list.stream() .sorted() .forEach(System.out::println);
|
4. distinct(去重)
1 2 3
| Stream.of(1, 2, 2, 3, 3) .distinct() .forEach(System.out::print);
|
5. peek(调试用)
1 2 3
| list.stream() .peek(System.out::println) .collect(Collectors.toList());
|
四、终结操作(终点)
1. collect(收集结果)
1 2 3
| List<String> result = list.stream() .filter(s -> s.length() > 5) .collect(Collectors.toList());
|
2. count / anyMatch / allMatch / noneMatch
1 2 3
| long count = list.stream().filter(s -> s.contains("a")).count();
boolean hasLong = list.stream().anyMatch(s -> s.length() > 10);
|
3. findFirst / findAny
1
| Optional<String> first = list.stream().findFirst();
|
4. reduce(规约)
1 2
| int sum = Stream.of(1, 2, 3, 4) .reduce(0, Integer::sum);
|
五、进阶操作
1. 分组 & 统计
1 2 3 4 5
| Map<Integer, List<String>> grouped = list.stream() .collect(Collectors.groupingBy(String::length));
Map<String, Long> counted = list.stream() .collect(Collectors.groupingBy(s -> s, Collectors.counting()));
|
2. flatMap(扁平化)
1 2 3 4 5 6 7 8
| List<List<String>> nested = Arrays.asList( Arrays.asList("A", "B"), Arrays.asList("C", "D") );
List<String> flat = nested.stream() .flatMap(List::stream) .collect(Collectors.toList());
|
3. 并行流(parallelStream)
1 2 3
| list.parallelStream() .map(String::toUpperCase) .forEach(System.out::println);
|
注意:并行流适合 CPU 密集型、无状态的操作,慎用在小数据集或 I/O 场景。
六、自定义对象的使用示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| class User { String name; int age;
}
List<User> users = Arrays.asList( new User("Alice", 20), new User("Bob", 25), new User("Charlie", 20) );
Map<Integer, List<User>> byAge = users.stream() .collect(Collectors.groupingBy(User::getAge));
|
七、Stream 使用注意事项
- Stream 只能使用一次,执行终结操作后不能再继续链式调用。
- 惰性执行:中间操作不会立刻执行,直到触发终结操作。
- 如果出现性能瓶颈或调试困难,可用
.peek() 或切换回传统方式测试。
- 尽量避免在 stream 中使用可变状态(如外部变量累加等)。
Stream 是提升代码表达力和性能的利器,掌握之后,很多集合操作都能变得更简洁优雅。
如果你有更高级的 Stream 应用(如多级分组、嵌套规约、复杂 map-reduce 等),欢迎评论区继续交流讨论。