Collectors.toList()返回ArrayList,保留顺序且允许重复;toSet()返回HashSet,不保证顺序且自动去重;groupingBy遇null元素或null键会抛NPE;averagingDouble存在浮点精度误差且空流需判Optional。

Collectors.toList() 和 toSet() 的实际差异
调用 Collectors.toList() 得到的是 ArrayList 实例,顺序保留、允许重复;而 Collectors.toSet() 返回的是 HashSet(JDK 11+ 默认),不保证顺序且自动去重。如果原始流含重复元素或对顺序敏感,选错会导致逻辑错误。
- 需要保持插入顺序且允许重复 → 用
toList() - 只需唯一值、不关心顺序 →
toSet()足够 - 要唯一值且需有序 → 改用
Collectors.toCollection(TreeSet::new)
用 Collectors.groupingBy 分组时的空指针风险
当流中存在 null 元素,且分组函数返回 null(例如对对象字段直接调用 obj.getName()),groupingBy() 会抛出 NullPointerException。它默认使用 HashMap 作底层容器,不支持 null 键。
- 安全做法:提前过滤掉
null元素,如.filter(Objects::nonNull) - 或改用带分类器判空的写法:
groupingBy(obj -> obj != null ? obj.getType() : "UNKNOWN") - 若必须接受
null键,得手动构造ConcurrentHashMap并传入下游收集器,但非常规场景
Collectors.averagingDouble 等统计收集器的精度陷阱
averagingDouble、summingInt 这类方法返回的是基本类型包装结果(如 Double),但它们内部用的是简单累加,**不处理浮点误差累积**,也不做高精度补偿。对大量小数求平均时,结果可能与手算或 BigDecimal 方式有微小偏差。
-
金融/计费等对精度敏感场景 → 别用
averagingDouble,改用Collectors.collectingAndThen(..., BigDecimal::doubleValue)配合自定义归约 - 普通业务指标统计 → 可用,但需明确接受 IEEE 754 浮点行为
- 注意返回值是
OptionalDouble,空流会返回OptionalDouble.empty(),直接getAsDouble()会抛异常
Double avg = list.stream()
.mapToDouble(Item::getPrice)
.average()
.orElse(0.0); // 必须处理 empty 情况
自定义 Collector 的性能开销在哪
写匿名 Collector.of() 或实现 Collector 接口,看似灵活,但容易忽略三个代价点:并发场景下 combiner 被频繁调用;finisher 若做深拷贝或格式化,会放大 GC 压力;中间容器(如 ArrayList)若未预估大小,反复扩容影响吞吐。
立即学习“Java免费学习笔记(深入)”;
- 并发流慎用含锁或共享状态的自定义收集器,优先走
toCollection(ArrayList::new)+ 后续处理 - 若只是转换格式(如 List → Map),用
Collectors.toMap()内置实现更稳 - 必须自定义时,把
supplier中的容器初始化加上初始容量,比如() -> new ArrayList(list.size())
groupingBy 遇到 null 直接崩,以及 averagingDouble 在空流时没判 Optional 就取值——这两处线上报错频率远高于 Collector 写法本身。










