Java 8引入的Stream API让集合操作变得优雅又高效。但很多人还在用传统的for循环,或者只用了stream().collect()这一招。
今天把Stream API的常用操作从基础到高级过一遍。
基础操作
java
List<String> names = Arrays.asList("张三", "李四", "王五", "赵六", "张三丰");
// 过滤
names.stream()
.filter(name -> name.length() <= 2)
.collect(Collectors.toList());
// ["张三", "李四", "王五", "赵六"]
// 转换
names.stream()
.map(String::length)
.collect(Collectors.toList());
// [2, 2, 2, 2, 3]
// 排序
names.stream()
.sorted()
.collect(Collectors.toList());
// 去重
Stream.of(1, 1, 2, 3, 3)
.distinct()
.collect(Collectors.toList());
// [1, 2, 3]
// 截取
IntStream.range(0, 100)
.limit(5)
.collect(Collectors.toList());
// [0, 1, 2, 3, 4]
// 跳过
IntStream.range(0, 10)
.skip(5)
.collect(Collectors.toList());
// [5, 6, 7, 8, 9]
聚合操作
java
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
// 求和
int sum = numbers.stream().mapToInt(Integer::intValue).sum();
// 最大/最小
Optional<Integer> max = numbers.stream().max(Integer::compareTo);
Optional<Integer> min = numbers.stream().min(Integer::compareTo);
// 计数
long count = numbers.stream().count();
// 平均
OptionalDouble avg = numbers.stream().mapToInt(Integer::intValue).average();
收集器(Collectors)
java
List<User> users = ...;
// 转Map
Map<Long, User> userMap = users.stream()
.collect(Collectors.toMap(User::getId, u -> u));
// 按key分组
Map<String, List<User>> groupByCity = users.stream()
.collect(Collectors.groupingBy(User::getCity));
// 分组并计数
Map<String, Long> countByCity = users.stream()
.collect(Collectors.groupingBy(User::getCity, Collectors.counting()));
// 拼接字符串
String names = users.stream()
.map(User::getName)
.collect(Collectors.joining(", "));
// "张三, 李四, 王五"
// 分区(满足条件/不满足条件)
Map<Boolean, List<User>> partition = users.stream()
.collect(Collectors.partitioningBy(u -> u.getAge() >= 18));
实战案例
案例一:复杂数据转换
java
// 原始数据:订单列表
List<Order> orders = ...;
// 需求:找出已支付订单中,金额最高的前5个用户的名字
List<String> result = orders.stream()
.filter(o -> "PAID".equals(o.getStatus()))
.sorted(Comparator.comparing(Order::getAmount).reversed())
.limit(5)
.map(Order::getUserName)
.collect(Collectors.toList());
案例二:扁平化(flatMap)
java
// 每个用户有多个地址
List<User> users = ...;
// 获取所有用户的地址(扁平化)
List<Address> allAddresses = users.stream()
.flatMap(user -> user.getAddresses().stream())
.collect(Collectors.toList());
// 对比:不用flatMap
List<Address> allAddresses = new ArrayList<>();
for (User user : users) {
allAddresses.addAll(user.getAddresses());
}
案例三:并行流
java
// 数据量大时用并行流
long count = IntStream.range(0, 1_000_000)
.parallel() // 开启并行
.filter(n -> n % 2 == 0)
.count();
// 注意:并行流不是所有场景都快
// 数据量小、操作简单、有状态操作时可能更慢
Stream vs for循环
| 对比 | Stream | for循环 |
|---|---|---|
| 可读性 | 链式调用,语义清晰 | 需要自己组织逻辑 |
| 并行化 | .parallel()一行搞定 |
需要手写多线程 |
| 性能 | 大数据量并行时有优势 | 小数据量略快 |
| 调试 | 不太方便 | 断点调试容易 |
| 副作用 | 不鼓励 | 随意 |
建议:简单操作用Stream,复杂逻辑或需要调试时用for循环。
Stream API让Java的集合操作从"怎么做"变成了"做什么",代码更声明式、更易读。
建议把常用操作记下来,写代码时先想"能不能用Stream"。