332. Java Stream API - Java Stream 实战进阶:按年份找出合作最多的作者对

332. Java Stream API - Java Stream 实战进阶:按年份找出合作最多的作者对


🎯 目标升级版

之前我们找出了合作最多的两位作者 ,现在我们想再进一层: 👉 按年份(inceptionYear)统计每年合作次数最多的作者对!


💡 核心思路

我们要构建一个这样的结构:

java 复制代码
Map<Integer, Map.Entry<PairOfAuthors, Long>>

其中:

  • Integer 是年份;
  • PairOfAuthors 是两位作者的组合;
  • Long 是他们在该年合写的文章数量。

🧰 技术点复用:我们已经有了这些组件

  1. 作者对生成器 toPairOfAuthors:将一篇文章中所有作者组合为 PairOfAuthors
  2. 分组计数器 groupingBy + counting:统计某个作者对出现的次数
  3. 最大值提取器:从 Map 中取出合作次数最多的那一对

🏗️ Step 1:创建中间 Collector

先把"作者对 -> 次数"的 collector 和"取最大"的 finisher 拆出来:

java 复制代码
// 构建频率直方图
Collector<PairOfAuthors, ?, Map<PairOfAuthors, Long>> groupingBy =
    Collectors.groupingBy(Function.identity(), Collectors.counting());

// 提取出现最多的作者对
Function<Map<PairOfAuthors, Long>, Map.Entry<PairOfAuthors, Long>> finisher =
    map -> {
    if (map.isEmpty()) {
        // 返回默认值而不是抛出异常
        return Map.entry(new PairOfAuthors(new Author("None"), new Author("None")), 0L);
    }
    return map.entrySet().stream()
        .max(Map.Entry.comparingByValue())
        .orElseThrow();
};

🧪 Step 2:合并为单个 collector:collectingAndThen

我们将 groupingBy + finisher 合成一个终极 Collector:

java 复制代码
Collector<PairOfAuthors, ?, Map.Entry<PairOfAuthors, Long>> pairOfAuthorsEntryCollector =
    Collectors.collectingAndThen(
        groupingBy,
        finisher
    );

collectingAndThen 可以让你对收集结果做后处理。


🧬 Step 3:让 flatMap 成为 Collector:flatMapping()

我们还可以将作者对的 flatMap 操作也变成 collector:

java 复制代码
Collector<Article, ?, Map.Entry<PairOfAuthors, Long>> flatMapping =
    Collectors.flatMapping(
        toPairOfAuthors,                     // 元素转换:每篇文章展开为作者对流
        pairOfAuthorsEntryCollector          // 收集逻辑:计数并提取最大对
    );

🔍 注意:flatMapping 只有在 JDK 12+ 才支持,需确保运行环境。


🧭 Step 4:按年份分组,应用 flatMapping 作为 downstream

java 复制代码
Map<Integer, Map.Entry<PairOfAuthors, Long>> result =
    articles.stream()
            .collect(Collectors.groupingBy(
                Article::inceptionYear,      // 分组依据:年份
                flatMapping                  // 每组处理逻辑
            ));

🎯 现在你就得到了每一年的"合作次数最多的作者对"。


🧪 示例输出(模拟)

java 复制代码
result.forEach((year, pair) -> {
    System.out.println(year + ": " +
        pair.getKey().first().name() + " + " +
        pair.getKey().second().name() + " -> " +
        pair.getValue() + " times");
});

🖨️ 假设输出:

java 复制代码
2019: Maria + Patricia -> 1 times
2020: Maria + James -> 2 times
2021: Patricia + Michael -> 2 times
2022: Maria + James -> 3 times

📦 总结归纳

步骤 技术点 说明
1️⃣ flatMapflatMapping() 把转换操作变为 collector
2️⃣ groupingBycollectingAndThen() 把聚合和提取最大合并
3️⃣ groupingBy(year, downstream) 多级分组并传入下游 collector
4️⃣ .orElseThrow() 安全性 记得对空数据做容错处理

💬 类比讲解

📦 想象你是在统计每年的合作之"最":

  • 每篇文章像是一张会议签到表;
  • 我们找出所有同时签名的两个人;
  • 然后按年份分组,统计谁签的最多;
  • 最后找出每年"最亲密搭档"。
相关推荐
徐小夕2 小时前
花了一周,3亿tokens,我开源了一款 Word 文档智能审查平台,文稿自动质检+可视化分析,告别低效人工审核
前端·算法·github
Ai拆代码的曹操2 小时前
Spring 事务 REQUIRES_NEW 嵌套调用:连接池翻倍的秘密
java·后端·spring
kyriewen3 小时前
别再乱用useEffect了——你写的10个里有8个不该存在
前端·javascript·react.js
Ivanqhz3 小时前
Rust &‘static str浅析
java·前端·javascript·rust
IT_陈寒4 小时前
SpringBoot这个分页坑,我踩了三天才爬出来
前端·人工智能·后端
颜酱4 小时前
05 | 召回前置准备:根据业务数据库生成各数据库(读取配置阶段)
前端·人工智能·后端
Wang's Blog4 小时前
Go-Zero项目开发24: 基于Bitmap实现群聊消息已读未读
开发语言·后端·golang
zandy10114 小时前
衡石 Agentic BI的ReAct 推理框架在 Agentic BI 中的工程化实践
前端·javascript·react.js
罗超驿4 小时前
JavaEE进阶之路:从Web架构原理到HTML标签全解析
前端·html·web·javaee
西安小哥4 小时前
从前端到AI工程师:一场跨越鸿沟的真实蜕变之旅
前端