一、基本概念
Collectors.collectingAndThen() 是一个收集器适配器,允许在收集完成后对结果进行转换。
java
// 方法签名
public static <T, A, R, RR> Collector<T, A, RR> collectingAndThen(
Collector<T, A, R> downstream, // 下游收集器
Function<R, RR> finisher // 最终转换函数
)
// 参数说明:
// T: 输入元素类型 (不变)
// A: 中间容器类型 (不变)
// R: 下游收集器的结果类型
// RR: 最终结果类型(finisher转换后)
其中这里面涉及到的范型参数可以参考下面的解析:
java
public interface Collector<T, A, R> {
// T: 流元素的类型(输入)
// A: 累加器的中间类型(容器)
// R: 最终结果的类型(输出)
Supplier<A> supplier(); // 创建容器A
BiConsumer<A, T> accumulator(); // 将T累加到A
BinaryOperator<A> combiner(); // 合并两个A(并行流)
Function<A, R> finisher(); // 将A转换为R
Set<Characteristics> characteristics();
}
示例分析:
java
List<String> list = Arrays.asList("a", "b", "c");
List<String> result = list.stream()
.collect(Collectors.collectingAndThen(
Collectors.toList(), // Collector<String, List<String>, List<String>>
Collections::unmodifiableList // Function<List<String>, List<String>>
));
// 类型推导:
// Collector<T=String, A=List<String>, R=List<String>>
// Function<R=List<String>, RR=List<String>>
// 最终 Collector<T=String, A=List<String>, RR=List<String>>
二、基本用法
示例1:收集为不可变集合
java
List<String> list = Arrays.asList("a", "b", "c");
// 转换为不可修改的List
List<String> unmodifiableList = list.stream()
.collect(Collectors.collectingAndThen(
Collectors.toList(), // 先收集为List
Collections::unmodifiableList // 再转换为不可修改
));
// 尝试修改会抛出异常
// unmodifiableList.add("d"); // UnsupportedOperationException
示例2:获取集合大小
java
List<String> list = Arrays.asList("a", "b", "c", "d");
// 直接计算集合大小
int size = list.stream()
.collect(Collectors.collectingAndThen(
Collectors.toList(), // 收集为List
List::size // 转换为大小
));
System.out.println(size); // 输出: 4
示例3:分组后执行操作
java
List<Person> people = Arrays.asList(
new Person("Alice", "New York"),
new Person("Bob", "New York"),
new Person("Charlie", "London")
);
// 按城市分组,并获取每个组的数量
Map<String, Integer> cityCount = people.stream()
.collect(Collectors.collectingAndThen(
Collectors.groupingBy(Person::getCity),
map -> {
// 对分组结果进行处理
Map<String, Integer> result = new HashMap<>();
map.forEach((city, list) -> result.put(city, list.size()));
return result;
}
));
示例4:获取最大值或最小值
java
List<Integer> numbers = Arrays.asList(1, 3, 5, 2, 4);
// 获取最大值,如果没有则返回默认值
Integer max = numbers.stream()
.collect(Collectors.collectingAndThen(
Collectors.maxBy(Integer::compareTo),
opt -> opt.orElse(-1) // 处理Optional
));
System.out.println(max); // 输出: 5
示例5:连接字符串并添加前缀后缀
java
List<String> words = Arrays.asList("Hello", "World", "Java");
// 用逗号连接,并添加方括号
String result = words.stream()
.collect(Collectors.collectingAndThen(
Collectors.joining(", "), // 先连接
s -> "[" + s + "]" // 再加括号
));
System.out.println(result); // 输出: [Hello, World, Java]
示例6:收集为Set并排序
java
List<Integer> numbers = Arrays.asList(5, 2, 5, 1, 3, 2);
// 去重后排序
List<Integer> sortedUnique = numbers.stream()
.collect(Collectors.collectingAndThen(
Collectors.toCollection(TreeSet::new), // 自动去重和排序
ArrayList::new // 转换为List
));
System.out.println(sortedUnique); // 输出: [1, 2, 3, 5]
示例7:多重转换
java
List<Employee> employees = Arrays.asList(
new Employee("Alice", 5000),
new Employee("Bob", 6000),
new Employee("Charlie", 5000)
);
// 按薪资分组 -> 获取名字列表 -> 排序
Map<Integer, List<String>> salaryToNames = employees.stream()
.collect(Collectors.collectingAndThen(
Collectors.groupingBy(
Employee::getSalary,
Collectors.mapping(Employee::getName, Collectors.toList())
),
map -> {
// 对每个分组的名字列表排序
map.replaceAll((k, v) -> {
Collections.sort(v);
return v;
});
return map;
}
));
示例8:数据库查询结果处理
java
// 查询用户列表,按部门分组,统计每个部门的平均工资
List<User> users = userRepository.findAll();
Map<String, Double> deptAvgSalary = users.stream()
.collect(Collectors.collectingAndThen(
Collectors.groupingBy(
User::getDepartment,
Collectors.averagingDouble(User::getSalary)
),
map -> {
// 保留两位小数
Map<String, Double> result = new LinkedHashMap<>();
map.forEach((dept, avg) ->
result.put(dept, Math.round(avg * 100.0) / 100.0));
return result;
}
));
示例9:订单统计
java
List<Order> orders = orderService.getRecentOrders();
// 统计每个用户的订单总金额,按金额降序排序
List<UserTotal> userTotals = orders.stream()
.collect(Collectors.collectingAndThen(
Collectors.groupingBy(
Order::getUserId,
Collectors.summingDouble(Order::getAmount)
),
map -> map.entrySet().stream()
.map(e -> new UserTotal(e.getKey(), e.getValue()))
.sorted(Comparator.comparing(UserTotal::getTotal).reversed())
.collect(Collectors.toList())
));
三、使用建议
"用 collectingAndThen 只做一件事,复杂的就分步,简单的直接做,保持代码干净。"
三个核心原则:
- 只做简单转换
- 一次只做一件事
- 数据量大才考虑使用
1. 简单原则
能用简单方式就不用复杂收集器
java
// ❌ 过度使用
List<String> result = list.stream()
.collect(collectingAndThen(
toList(),
Collections::unmodifiableList
));
// ✅ 简单方式(Java 10+)
List<String> result = List.copyOf(list);
// 或
List<String> result = Collections.unmodifiableList(new ArrayList<>(list));
2. 保持函数纯净
不要在转换函数中有副作用
java
// ❌ 有副作用
Map<String, List<Person>> result = persons.stream()
.collect(collectingAndThen(
groupingBy(Person::getCity),
map -> {
map.forEach((k, v) -> System.out.println(k)); // 副作用
return map;
}
));
// ✅ 纯函数
Map<String, List<Person>> result = persons.stream()
.collect(groupingBy(Person::getCity));
// 副作用放到后面
result.forEach((k, v) -> System.out.println(k));
3. 性能意识
小数据不要用,大数据才考虑
java
List<String> smallList = Arrays.asList("a", "b", "c"); // 只有3个元素
// ❌ 没必要用 collectingAndThen
List<String> result1 = smallList.stream()
.collect(collectingAndThen(toList(), Collections::unmodifiableList));
// ✅ 直接创建不可变集合
List<String> result2 = List.copyOf(smallList); // Java 10+
// 或
List<String> result3 = Collections.unmodifiableList(new ArrayList<>(smallList));
// ✅ 大数据集用 collectingAndThen
List<String> largeList = getLargeList(); // 假设有上万条
List<String> result = largeList.stream()
.collect(collectingAndThen(toList(), Collections::unmodifiableList));
4. 常用场景才用
不要为了用而用
java
// ✅ 真正有用的场景:
// 1. 收集后立即转换
List<String> result = list.stream()
.collect(collectingAndThen(
toList(),
Collections::unmodifiableList // 立即转为不可变
));
// 2. 处理 Optional 结果
String max = list.stream()
.collect(collectingAndThen(
maxBy(String::compareTo),
opt -> opt.orElse("default") // 解包Optional
));
// 3. 连接字符串后包装
String joined = list.stream()
.collect(collectingAndThen(
joining(", "),
s -> "[" + s + "]" // 添加括号
));
// ❌ 没必要用的场景:
// - 只是简单的收集
// - 后续还有更多处理步骤
// - 转换逻辑很复杂
5. 复杂逻辑就分步
不要都塞进 collectingAndThen
java
// ❌ 过于复杂
List<String> result = list.stream()
.collect(collectingAndThen(
toList(),
list -> {
// 一大堆复杂逻辑
list.sort(Comparator.reverseOrder());
list.replaceAll(String::toUpperCase);
return Collections.unmodifiableList(list);
}
));
// ✅ 分步处理更清晰
// 步骤1:收集和排序
List<String> temp = list.stream()
.sorted(Comparator.reverseOrder())
.collect(toList());
// 步骤2:转换
temp.replaceAll(String::toUpperCase);
// 步骤3:转为不可变
List<String> result = Collections.unmodifiableList(temp);