Collectors 全家桶:toList/joining/groupingBy(AI 辅助学习 Java 8)

0 阅读3分钟

上周学了 Stream 基础操作。今天深入 Stream 的终端操作核心——Collectors。它是将流收集为各种数据结构的工具类,掌握它才能真正发挥 Stream 的威力。


一、Collectors 是什么?

Collectors 是一个工具类,提供了大量预定义的收集器。每个收集器都是一个 Collector 接口的实现,用于将 Stream 中的元素收集到某种结果容器中。

核心分类

类别收集器作用
转集合toList / toSet / toMap / toCollection收集为集合
拼接joining拼接为字符串
分组groupingBy按条件分组
分区partitioningBy按 true/false 分区
统计counting / summing / averaging / summarizing聚合统计
映射mapping收集时转换

二、转集合:toList / toSet / toMap

toList:收集为 List

List<String> titles = posts.stream()
        .map(Post::getTitle)
        .collect(Collectors.toList());

toSet:收集为 Set(自动去重)

Set<String> authors = posts.stream()
        .map(Post::getAuthor)
        .collect(Collectors.toSet());

toMap:收集为 Map

// Map<标题, 阅读量>
Map<String, Integer> titleViews = posts.stream()
        .collect(Collectors.toMap(Post::getTitle, Post::getViews));

💡 注意:toMap 要求 key 唯一。如果有重复 key,需要传入第三个参数处理冲突:

Collectors.toMap(keyMapper, valueMapper, (oldVal, newVal) -> newVal)

三、joining:字符串拼接

// 简单拼接
String all = posts.stream()
        .map(Post::getTitle)
        .collect(Collectors.joining(", "));
// "Lambda 入门, Stream 详解, Vue3 API, ..."

// 带前后缀
String tagged = posts.stream()
        .map(Post::getTitle)
        .collect(Collectors.joining(" | ", "【", "】"));
// "【Lambda 入门 | Stream 详解 | ...】"

三个参数分别是:分隔符、前缀、后缀。等价于 JS 的 arr.join(", ") 但更灵活。

四、groupingBy:分组

这是 Collectors 最强大的功能之一。

基础分组

// 按作者分组
Map<String, List<Post>> byAuthor = posts.stream()
        .collect(Collectors.groupingBy(Post::getAuthor));

分组 + 计数

Map<String, Long> authorCount = posts.stream()
        .collect(Collectors.groupingBy(Post::getAuthor, Collectors.counting()));
// {张三=3, 李四=2, 王五=2}

分组 + 平均值

Map<String, Double> authorAvgViews = posts.stream()
        .collect(Collectors.groupingBy(Post::getAuthor,
                Collectors.averagingInt(Post::getViews)));
// {张三=243.3, 李四=450.0, 王五=275.0}

多级分组

// 先按作者分,再按是否发布分
Map<String, Map<Boolean, Long>> result = posts.stream()
        .collect(Collectors.groupingBy(
                Post::getAuthor,
                Collectors.groupingBy(Post::isPublished, Collectors.counting())
        ));
// 张三: {已发布=2, 未发布=1}
// 李四: {已发布=2}

五、partitioningBy:分区

分区是分组的一种特殊情况,按 true/false 分为两组。

Map<Boolean, List<Post>> partitioned = posts.stream()
        .collect(Collectors.partitioningBy(Post::isPublished));

// partitioned.get(true)  -> 已发布的列表
// partitioned.get(false) -> 未发布的列表

分区 + 统计

Map<Boolean, Double> avgViews = posts.stream()
        .collect(Collectors.partitioningBy(Post::isPublished,
                Collectors.averagingInt(Post::getViews)));
// true -> 已发布平均阅读 350
// false -> 未发布平均阅读 215

六、summarizing:一键获取所有统计信息

IntSummaryStatistics stats = posts.stream()
        .collect(Collectors.summarizingInt(Post::getViews));

stats.getCount();    // 7
stats.getSum();      // 2180
stats.getAverage();  // 311.4
stats.getMax();      // 500
stats.getMin();      // 180

一个方法同时获取计数、总和、平均值、最大值、最小值,比分别调用五次高效得多。

七、运行结果

=== toList / toSet / toMap ===
  toList: [Lambda 入门, Stream 详解, Vue3 API, React Hooks, ...]
  toSet (作者去重): [李四, 张三, 王五]
  toMap (标题->阅读量): {Lambda 入门=200, Vue3 API=500, ...}

=== groupingBy:分组 ===
  张三: 3 篇
  李四: 2 篇
  王五: 2 篇
  按作者平均阅读: {李四=450.0, 张三=243.3, 王五=275.0}

=== partitioningBy:分区 ===
  已发布: 5 篇
  未发布: 2 篇
  已发布平均阅读: 350
  未发布平均阅读: 215

=== summarizingInt:汇总统计 ===
  文章总数: 7
  总阅读量: 2180
  平均阅读: 311
  最高阅读: 500
  最低阅读: 180

=== 多级分组 ===
  作者->发布状态->数量:
    张三: 已发布=2, 未发布=1
    李四: 已发布=2, 未发布=null
    王五: 已发布=1, 未发布=1

八、Collectors 速查表

收集器返回类型用途
toListList收集为列表
toSetSet收集为集合(去重)
toMapMap<K,V>收集为映射
joiningString拼接为字符串
groupingByMap<K,List>按条件分组
partitioningByMap<Boolean,List>按真假分区
countingLong计数
summingIntInteger求和
averagingIntDouble平均值
summarizingIntIntSummaryStatistics汇总统计
mapping配合 groupingBy 使用收集时转换

九、下篇预告

周四我们将深入 flatMap 的高级用法:一对多场景、标签提取、嵌套集合处理等实战技巧。


系列文章导航

更多请关注我的微信公众号,感谢!

微信图片_20260427132414_18_737.jpg