Collectors类是提供Collector实例的工厂,非工具类;toMap遇重复key抛IllegalStateException,需提供合并函数;groupingBy的value类型由下游收集器决定;joining对null零容忍;toList返回不可变List,toCollection可自定义容器类型。

Collectors 类不是工具类,而是为 Stream.collect() 提供收集器(Collector)的工厂集合 —— 直接调用它的静态方法返回的是 Collector 实例,不是结果本身。
toMap 为什么总抛 IllegalStateException: Duplicate key
这是最常见报错,发生在键冲突时:toMap 默认不处理重复键。除非显式传入合并函数,否则遇到相同 key 就崩溃。
- 安全写法必须提供第三个参数:例如
toMap(Person::getName, Person::getAge, (v1, v2) -> v1),表示保留第一个值 - 如果想聚合(如求和),可写
(v1, v2) -> v1 + v2 - 注意:
keyMapper和valueMapper函数不能返回null,否则触发NullPointerException
groupingBy 分组后 Map 的 value 类型由下游收集器决定
groupingBy 本身只做分组,value 是 List;但加下游收集器就能改变结构,比如转成计数、求和或嵌套分组。
- 默认:
Collectors.groupingBy(Person::getCity)→Map> - 计数:
groupingBy(Person::getCity, Collectors.counting())→Map - 求平均年龄:
groupingBy(Person::getCity, averagingInt(Person::getAge)) - 多级分组:
groupingBy(Person::getCity, groupingBy(p -> p.getAge() >= 18 ? "adult" : "minor"))
joining 用于字符串拼接,但 null 元素会直接抛 NullPointerException
joining 只接受 CharSequence,对 null 零容忍,连空字符串都不算安全。
立即学习“Java免费学习笔记(深入)”;
- 基础用法:
collect(joining(", ")),适用于Stream - 若元素可能为
null,必须提前过滤或映射:map(Objects::toString).collect(joining(", ")) - 带前缀/后缀:
joining("-", "[", "]")→[a-b-c]
toCollection 和 toList 的区别不只是返回类型
toList() 返回的是不可修改的 List(JDK 16+ 是 ImmutableCollections.ListN),而 toCollection 让你完全控制容器类型和实例化方式。
- 需要可变列表?写
toCollection(ArrayList::new) - 需要线程安全?写
toCollection(CopyOnWriteArrayList::new) - 需要有序去重?写
toCollection(LinkedHashSet::new)(配合distinct()或自行去重逻辑) - 注意:
toList()在 JDK 16 前返回ArrayList,之后改为不可变实现 —— 如果代码依赖list.add(),必须改用toCollection
MapnameToLength = Stream.of("Alice", "Bob", "Charlie") .collect(Collectors.toMap( s -> s, String::length, (len1, len2) -> len1 // 解决重复 key 冲突 ));
真正容易被忽略的,是收集器的「惰性组合」特性:像 groupingBy 套 mapping 再套 counting,每层都新增一次遍历开销;而手写 forEach 累加可能更高效 —— 别迷信链式写法,先看数据量和性能要求。










