Stream流式使用
一、Stream是什么
Stream是支持顺序和并行聚合操作的元素序列。简单来说Stream 就像一条"流水线"——你把数据放上去,经过一道道工序(筛选、加工、转换),最后出来你想要的结果。
二、核心结构:
数据源 → 中间操作(懒执行)→ 终端操作(触发执行)。中间操作只是“记下工序”,不会真的跑,直到你调用终端操作(如 collect、forEach)才一次性执行。
结果 = 数据源.stream() // 1. 拿到流
.中间操作1() // 2. 加工(可多个,懒执行)
.中间操作2()
.终端操作(); // 3. 触发执行,出结果
三、Strean的使用
1. 怎么拿到流(源头)
Stream 不能 new,只能从数据源获取:
list.stream() // 集合 → Stream<T>
Arrays.stream(array) // 数组 → Stream<T>
Stream.of("a", "b", "c") // 直接创建
IntStream.range(1, 10) // 生成 1~9 的 int 流
Files.lines(Paths.get("f.txt")) // 文件每行 → Stream<String>
2. 中间操作(返回 Stream,可链式)
这些方法定义在 Stream / IntStream 等接口上,懒执行:
filter(Predicate) — 筛选
map(Function) — 转换
flatMap(Function) — 转换并"摊平"(比如 List<List> → 一个流)
distinct() — 去重
sorted() / sorted(Comparator) — 排序
limit(n) / skip(n) — 取前 n 个 / 跳过前 n 个
peek(Consumer) — 偷看每个元素(调试用)
3. 终端操作(触发执行,返回结果)
collect(Collector) — 收集成 List/Set/Map 等
forEach(Consumer) — 遍历
count() — 计数
reduce(...) — 归约(求和、求积、拼字符串)
anyMatch / allMatch / noneMatch — 条件判断
findFirst / findAny — 找元素
min / max — 最值
sum() / average() — 只有 IntStream/LongStream/DoubleStream 才有
4.辅助工具
Collectors(java.util.stream.Collectors) 配合 collect 用,是静态工具类,不是流:
Collectors.toList() // 收成 List
Collectors.toSet() // 收成 Set
Collectors.toMap(k, v) // 收成 Map
Collectors.groupingBy(...) // 分组 → Map<K, List<T>>
Collectors.joining(",") // 拼成字符串
Collectors.counting() / summingInt() // 统计
三、最常见的使用场
- 筛选 + 收集(filter + collect) 从用户列表里找出成年人:
List<User> adults = users.stream()
.filter(u -> u.getAge() >= 18)
.collect(Collectors.toList());
- 转换(map) 把用户列表转成名字列表:
List<String> names = users.stream()
.map(User::getName)
.collect(Collectors.toList());
对象转另一个对象(如 User → UserDTO):
List<UserDTO> dtos = users.stream()
.map(u -> new UserDTO(u.getId(), u.getName()))
.collect(Collectors.toList());
- 筛选 + 转换 + 排序(链式)
List<String> result = users.stream()
.filter(u -> u.getAge() >= 18)
.map(User::getName)
.sorted()
.collect(Collectors.toList());
- 去重、取前几个、跳过
list.stream().distinct().collect(Collectors.toList()); // 去重
list.stream().limit(10).collect(Collectors.toList()); // 前 10 个
list.stream().skip(5).collect(Collectors.toList()); // 跳过前 5 个
- 判断条件(anyMatch / allMatch / noneMatch)
boolean hasAdult = users.stream().anyMatch(u -> u.getAge() >= 18); // 有没有成年人
boolean allAdult = users.stream().allMatch(u -> u.getAge() >= 18); // 是不是都成年
boolean noChild = users.stream().noneMatch(u -> u.getAge() < 18); // 有没有未成年
- 找元素(findFirst)
Optional<User> first = users.stream()
.filter(u -> u.getAge() > 30)
.findFirst();
// Optional 要这样取值,避免空指针
String name = first.map(User::getName).orElse("无");
- 统计:计数、求和、最值
long count = users.stream().filter(u -> u.getAge() >= 18).count();
// 求和/平均/最值,用 mapToInt 转成 IntStream 更方便
int totalAge = users.stream().mapToInt(User::getAge).sum();
double avgAge = users.stream().mapToInt(User::getAge).average().orElse(0);
Optional<User> oldest = users.stream().max(Comparator.comparing(User::getAge));
- 分组(groupingBy,业务里超常用) 按城市给用户分组:
Map<String, List<User>> byCity = users.stream()
.collect(Collectors.groupingBy(User::getCity));
分组后统计每组人数:
Map<String, Long> cityCount = users.stream()
.collect(Collectors.groupingBy(User::getCity, Collectors.counting()));
- 收成 Map(toMap) id → User 的映射:
Map<Long, User> idMap = users.stream()
.collect(Collectors.toMap(User::getId, u -> u));
// 如果 key 可能重复,要加合并函数,否则会抛异常
Map<String, User> nameMap = users.stream()
.collect(Collectors.toMap(User::getName, u -> u, (a, b) -> a));
- 拼字符串(joining)
String names = users.stream()
.map(User::getName)
.collect(Collectors.joining(", ", "[", "]")); // 结果形如 [张三, 李四]
- 归约(reduce) 求所有年龄之和(也可以用 mapToInt().sum()):
int sum = users.stream()
.map(User::getAge)
.reduce(0, Integer::sum);
- flatMap 摊平嵌套集合 比如每个用户有多个订单,想拿到所有订单:
List<Order> allOrders = users.stream()
.flatMap(u -> u.getOrders().stream())
.collect(Collectors.toList());