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️⃣ flatMap ➜ flatMapping() 把转换操作变为 collector
2️⃣ groupingBy ➜ collectingAndThen() 把聚合和提取最大合并
3️⃣ groupingBy(year, downstream) 多级分组并传入下游 collector
4️⃣ .orElseThrow() 安全性 记得对空数据做容错处理

💬 类比讲解

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

  • 每篇文章像是一张会议签到表;
  • 我们找出所有同时签名的两个人;
  • 然后按年份分组,统计谁签的最多;
  • 最后找出每年"最亲密搭档"。
相关推荐
可乐鸡翅yeah_5 小时前
业务中 M3U8 水印相关坑,硬水印和动态水印区别
前端·网络·数据库·ffmpeg·m3u8在线
lerhxx5 小时前
AI 应用如何高效优雅地恢复中断?—— "连接解耦 + 状态持久化"
前端·javascript
计算机魔术师5 小时前
METR 演示 AI 智能体如何篡改 Inspect 评估记录以掩盖不当行为
前端
llqbzllll5 小时前
Spring AI 工具调用不是反射一下就结束:用 2.0.1 跑通失败恢复与调用上限
人工智能·后端
前端snow6 小时前
ai agent --- 异步处理之 Rabbit MQ
前端
狼爷6 小时前
Rust/Go/Java/Python/PHP 大比拼:负载下后端框架到底差多少?
java·后端·编程语言
念何架构之路6 小时前
zap扩展生态与总结
java·前端·数据库
独孤九剑打醒他6 小时前
【原创开源】【概念设计】源 - 栅 - 漏 - 栅 - 源 横向双栅 MOS,低压交流多值逻辑芯片探索
前端·其他·架构·开源·硬件工程
mantou1327 小时前
我给 AI Agent 做了个「油猴」:让 Claude Code / Codex 直接用你已登录的浏览器
前端·javascript·后端
默_笙7 小时前
🍕 一个主编、三个工种、两本手册:搭一支 AI 调研队
前端·javascript