Appearance
2026-08-29 · 集合的流水线处理:过滤、转换、排序、分组、收集,一条链子写完。
Java Stream 流
1. Stream 是什么
Stream(流):对集合数据做批量处理的"流水线"。不存储数据,只是把集合"流"过去,边流边处理。
原始数据 ──▶ [filter 过滤] ──▶ [map 转换] ──▶ [sorted 排序] ──▶ [collect 收集]
每一站都是操作,最后收集成新结果和 for 循环的区别:for 是"命令式"——你一步步告诉电脑怎么走;Stream 是"声明式"——你只说"过滤、转换、排序",细节它自己安排。
2. 创建 Stream 的三种方式
java
import java.util.*;
import java.util.stream.*;
// ① 集合 → stream(最常用)
List<String> list = List.of("a", "b", "c");
Stream<String> s1 = list.stream();
// ② 数组 → stream
String[] arr = {"a", "b"};
Stream<String> s2 = Arrays.stream(arr);
// ③ 直接造
Stream<String> s3 = Stream.of("a", "b", "c");Stream 用完即弃(一次性的):流操作完就没了,想再用要重新从集合拿。不能"倒回去"。
3. 中间操作:过滤、转换、排序(可无限链)
中间操作只是"布置流水线",不真正执行,最后必须跟一个终结操作才跑起来。
3.1 filter:过滤(Predicate 判断)
java
List<Integer> nums = List.of(1, 2, 3, 4, 5, 6);
// 只要偶数
nums.stream()
.filter(n -> n % 2 == 0) // 留下满足条件的
.forEach(System.out::println); // 2 4 63.2 map:转换(Function 映射)
java
List<String> names = List.of("alice", "bob");
// 每个元素转大写,收集成新列表
List<String> upper = names.stream()
.map(String::toUpperCase) // 方法引用,等价于 s -> s.toUpperCase()
.collect(Collectors.toList());
// [ALICE, BOB]3.3 sorted:排序
java
List<Integer> nums = List.of(3, 1, 2);
nums.stream()
.sorted() // 自然升序
.sorted((a, b) -> b - a) // 自定义:降序(Comparator Lambda)
.forEach(System.out::println); // 3 2 13.4 distinct:去重
java
List<Integer> nums = List.of(1, 2, 1, 3, 2);
nums.stream().distinct().forEach(System.out::println); // 1 2 33.5 limit / skip:限量/跳过
java
// 取前 2 个 / 跳过前 1 个
nums.stream().limit(2); // 1 2
nums.stream().skip(1); // 2 3 4...3.6 链起来用(核心体验)
java
// 需求:数字里过滤出偶数 → 转成字符串 → 排序 → 取前 2 个
List<Integer> nums = List.of(5, 2, 8, 1, 9, 4);
List<String> result = nums.stream()
.filter(n -> n % 2 == 0) // 2 8 4
.map(n -> "数" + n) // 数2 数8 数4
.sorted() // 数2 数4 数8
.limit(2) // 数2 数4
.collect(Collectors.toList());
System.out.println(result); // [数2, 数4]这就是 Stream 的优势:一个需求一条链写完,不用写一堆 for + if + 临时变量。
4. 终结操作:真正执行(流到头了)
| 方法 | 作用 | 示例 |
|---|---|---|
forEach | 遍历执行 | list.stream().forEach(System.out::println) |
collect | 收进新集合 | .collect(Collectors.toList()) |
count | 数个数 | list.stream().filter(...).count() |
anyMatch | 有任意一个满足? | .anyMatch(n -> n > 5) |
allMatch | 全部满足? | .allMatch(n -> n > 0) |
noneMatch | 一个都不满足? | .noneMatch(n -> n < 0) |
min/max | 最大最小值 | .mapToInt(Integer::intValue).max() |
java
List<Integer> nums = List.of(1, 2, 3, 4);
boolean hasBig = nums.stream().anyMatch(n -> n > 3); // true
long count = nums.stream().filter(n -> n % 2 == 0).count(); // 2Collectors 常用收法:
java
// 收集成 List(最常用)
List<String> list = stream.collect(Collectors.toList());
// 收集成 Set(自动去重)
Set<String> set = stream.collect(Collectors.toSet());
// 拼接字符串
String joined = stream.collect(Collectors.joining(", ")); // "a, b, c"
// 统计:个数/和/平均
Collectors.counting();
Collectors.summingInt(s -> s.length());5. 分组:按条件把元素分类(嵌套 Map 实战)
按用户年龄分组,结果就是 Map<Integer, List<Person>>(整数年龄 → 这个年龄的一群人)——这正是集合嵌套最典型的场景:
java
class Person { String name; int age; }
List<Person> people = List.of(
new Person("张三", 20),
new Person("李四", 30),
new Person("王五", 20)
);
// 按 age 分组:key=年龄,value=这个年龄的所有人
Map<Integer, List<Person>> byAge = people.stream()
.collect(Collectors.groupingBy(p -> p.age));
// {20=[张三, 王五], 30=[李四]} ← 嵌套结构:Map 里套 List嵌套集合的读写(开发中常见):
java
// 创建:里面再放一个集合当 value
Map<String, List<String>> map = new HashMap<>();
map.put("水果", new ArrayList<>(List.of("苹果", "香蕉")));
// 读取
List<String> fruits = map.get("水果"); // [苹果, 香蕉]
String first = map.get("水果").get(0); // 苹果(先按 key 拿 List,再按下标拿元素)
// 遍历嵌套 Map:两层循环
for (Map.Entry<String, List<String>> entry : map.entrySet()) {
System.out.println(entry.getKey()); // 组名
for (String item : entry.getValue()) { // 组内元素
System.out.println(" " + item);
}
}嵌套就一句话:value 本身又是集合。
Map<K, List<V>>、Map<K, Map<K2, V>>都一样,一层层剥开读。
6. 什么时候用 Stream,什么时候用 for
| 场景 | 用哪个 |
|---|---|
| 过滤/转换/排序/分组一条龙 | Stream(清晰、少代码) |
| 遍历时还要改其他变量、中断退出 | for 循环 |
| 代码要给别人看、团队不熟 Stream | for 循环(可读性优先) |
| 大数据量(百万级) | Stream 并行流 parallelStream() 可以试,但先别碰 |
判断标准:一条链能表达完就用 Stream;逻辑复杂、要打断、要中间状态,回到 for。
7. 本篇小结
- Stream = 集合的流水线:中间操作(filter/map/sorted/distinct/limit)链式布置,终结操作(forEach/collect/count)才执行
- 中间操作接的都是函数式接口:filter→Predicate、map→Function、sorted→Comparator
collect(Collectors.toList())是最常用收尾;groupingBy 一键分组- 集合嵌套 = value 是集合:
Map<String, List<T>>一层层剥开 - 简单链用 Stream,复杂流程用 for,不要强行使用 Stream