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() 安全性 记得对空数据做容错处理

💬 类比讲解

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

  • 每篇文章像是一张会议签到表;
  • 我们找出所有同时签名的两个人;
  • 然后按年份分组,统计谁签的最多;
  • 最后找出每年"最亲密搭档"。
相关推荐
年轻的砖头7 分钟前
以Ajax方式提交整个表单
前端·javascript·ajax
L-影21 分钟前
FastAPI CORS 跨域:Web 世界的“小区门禁”与“访客通行证”
前端·javascript·fastapi
stereohomology26 分钟前
已进入初赛提交Demo的两个项目:(2)单独html钢琴
前端·html·why不coding
源图客33 分钟前
Claude Code基础使用
服务器·前端·数据库
布朗克16837 分钟前
Go 入门到精通-09-复合类型之Map
开发语言·后端·golang·map
asdzx671 小时前
在 React 中轻松实现 PDF 转 Word:纯前端 WASM 方案实战
前端·react.js·pdf
程序员爱钓鱼1 小时前
Rust 开发环境安装(Windows|macOS|Linux)
后端·rust
爱勇宝9 小时前
第 1 章:别把“需求文档”当成真正的需求
前端·后端·程序员
梨子同志10 小时前
常用命令
前端
梨子同志10 小时前
React 状态管理
前端