在Java中Collectors类常用方法有哪些_Java流结果收集说明

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
  • 注意:keyMappervalueMapper 函数不能返回 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 零容忍,连空字符串都不算安全。

  • 基础用法: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
Map nameToLength = Stream.of("Alice", "Bob", "Charlie")
    .collect(Collectors.toMap(
        s -> s,
        String::length,
        (len1, len2) -> len1 // 解决重复 key 冲突
    ));

正容易被忽略的,是收集器的「惰性组合」特性:像 groupingBymapping 再套 counting,每层都新增一次遍历开销;而手写 forEach 累加可能更高效 —— 别迷信链式写法,先看数据量和性能要求。