Collectors.mapping 是 groupingBy 的下游收集器,用于分组后对每组元素统一映射转换再收集;它不参与分组键计算,仅将 T 映射为 U 后交由下游收集器(如 toList、joining)处理,需嵌套使用且映射函数不可返回 null。

在 Java Stream API 中,Collectors.mapping 的作用不是“单独使用”,而是作为 groupingBy 的下游收集器(downstream collector),用来在分组完成之后、对每个分组内部的元素做**统一转换**,再交给最终的收集逻辑(比如转成 List、Set、拼接字符串等)。它解决的是“分组后还要把每个元素变成另一种形式”这个常见需求,避免先分组再遍历映射的冗余步骤。
mapping 的核心定位:分组后的值转换器
mapping 本身不产生 Map,也不参与分组键的计算。它只负责:对每个分组内已归集好的元素列表,逐个应用一个映射函数(Function<t u></t>),把原始元素 T 转成目标类型 U,然后把这些 U 交给后续的下游收集器(如 toList()、toSet()、joining() 等)处理。
它的签名是:Collectors.mapping(Function<t u>, Collector<u r>)</u></t>
第一个参数是映射函数,第二个参数才是真正的“怎么收这些映射后的值”。
典型组合:groupingBy + mapping + toList
这是最常用场景——按某属性分组,但每组不要原始对象,只要某个字段的值构成的列表。
- 比如用户列表按城市分组,但每组只保留姓名字符串:
Map<String, List<String>> cityToNames = users.stream()<br>
.collect(Collectors.groupingBy(<br>
User::getCity,<br>
Collectors.mapping(User::getName, Collectors.toList())<br>
));
这里 User::getCity 是分组键;User::getName 是映射函数;Collectors.toList() 是最终如何收集映射后的姓名字符串。
立即学习“Java免费学习笔记(深入)”;
搭配其他下游收集器更灵活
mapping 可以和多种下游收集器配合,不只是 toList:
- 拼接成逗号分隔字符串:
Collectors.mapping(User::getEmail, Collectors.joining(", ")) - 转成去重集合:
Collectors.mapping(User::getDepartment, Collectors.toSet()) - 统计映射后值的数量(比如统计各城市有多少不同邮箱域):
Collectors.mapping(u -> u.getEmail().split("@")[1], Collectors.counting())
注意两个常见陷阱
一是映射函数不能返回 null,否则会抛 NullPointerException(因为下游收集器通常不接受 null 元素);
二是 mapping 必须嵌套在 groupingBy 的下游参数里,不能写成两层 collect —— Stream 只能有一个终止操作,mapping 是辅助型收集器,必须依附于主收集器如 groupingBy 或 toMap。


















