Collectors.toList() 返回可变ArrayList,支持重复和null;toSet() 返回无序去重Set,遇null抛NPE;toMap() 遇重复key抛DuplicateKeyException;joining() 遇null直接抛NPE;应依约束选方法。

Collectors.toList() 和 toSet() 的行为差异
调用 Collectors.toList() 会返回一个可变的 ArrayList,允许后续添加元素;而 Collectors.toSet() 返回的是不可保证顺序、也不保证具体实现类的 Set(JDK 17+ 多为 LinkedHashSet,但不应依赖),且自动去重。如果流中含 null,toSet() 在大多数实现下会抛 NullPointerException,但 toList() 不会。
- 需要保留插入顺序且允许重复 → 用
toList() - 只需去重、不关心顺序 →
toSet()可用,但注意空值风险 - 要控制具体集合类型(如必须是
TreeSet)→ 改用Collectors.toCollection(TreeSet::new)
toMap() 容易触发的 DuplicateKeyException
Collectors.toMap(keyMapper, valueMapper) 要求 key 不能重复,一旦流中两个元素映射出相同 key,立刻抛 IllegalStateException: Duplicate key。这不是运行时异常,无法靠 try-catch 粗暴吞掉——它来自收集器内部的合并逻辑失败。
- 明确允许重复 key?改用三参数版:
toMap(keyMapper, valueMapper, (v1, v2) -> v1),第三个参数是冲突时的取舍策略 - 想保留所有值?用
Collectors.groupingBy(keyMapper, Collectors.mapping(valueMapper, Collectors.toList())) - key 来源字段可能为 null?先
filter(Objects::nonNull)或用Objects.toString(obj.getField(), "")做兜底
joining() 处理 null 元素的静默截断问题
Collectors.joining(", ") 遇到流中任意元素为 null,会直接抛 NullPointerException。它不会跳过 null、也不会转成字符串 "null",而是立即中断收集。
- 安全拼接:先
map(Objects::toString)或map(s -> s == null ? "" : s) - 带前缀/后缀?用三参数重载:
joining(", ", "[", "]") - 要按字段拼接对象?写
mapping(person -> person.getName(), joining(", ")),别漏掉mapping层
自定义收集器用 toCollection() 还是 reducing()?
多数场景优先选 Collectors.toCollection(Supplier):它只负责构造容器并逐个 add,语义清晰、性能好、无状态。而 reducing() 是归约操作,需提供初始值、累加器和组合器,适合计算型场景(如 sum、max),不适合构建集合。
立即学习“Java免费学习笔记(深入)”;
- 要返回
LinkedList?用toCollection(LinkedList::new) - 要初始化容量避免扩容?
toCollection(() -> new ArrayList(100)) - 真需要 reduce 逻辑(比如合并多个 Map)?才用
reducing,否则容易写出线程不安全或低效代码
Collectors 的核心不是“能做什么”,而是“每种方法隐含的约束”——toMap 拒绝重复 key,joining 拒绝 null,toSet 拒绝顺序保证。漏掉这些前提,调试时往往卡在“为什么结果不对”,而不是“怎么写”。










