331. Java Stream API - Java Stream 实战案例:找出合作最多的作者对

0 阅读2分钟

331. Java Stream API - Java Stream 实战案例:找出合作最多的作者对

🎯 目标

在一堆文章(Article)中,找出一起合作写文章次数最多的两个作者(Author)


🧩 数据模型设计

我们有三个核心模型:

// 作者,支持按名字排序
record Author(String name) implements Comparable<Author> {
    public int compareTo(Author other) {
        return this.name.compareTo(other.name);
    }
}

// 文章
record Article(String title, int inceptionYear, List<Author> authors) {}

// 作者对(避免重复,如 A-B 和 B-A 视为同一对)
record PairOfAuthors(Author first, Author second) {
    public static Optional<PairOfAuthors> of(Author first, Author second) {
        // 确保作者对排序一致(避免 A-B 和 B-A)
        if (first.compareTo(second) > 0) {
            return Optional.of(new PairOfAuthors(first, second));
        } else {
            return Optional.empty();
        }
    }
}

📌 PairOfAuthors.of()Optional 表示“是否是有效的作者对”,这样可以方便后续的流式处理。


🏗️ 构建作者对流

第一步是:从每篇文章中提取所有合法的作者对。

BiFunction<Article, Author, Stream<PairOfAuthors>> buildPairOfAuthors =
    (article, firstAuthor) ->
        article.authors().stream()
               .flatMap(secondAuthor ->
                   PairOfAuthors.of(firstAuthor, secondAuthor).stream());

🔍 如果 PairOfAuthors.of() 返回 Optional.empty(),那么 stream() 就是空流,否则是单元素流。这样可以优雅地处理无效对。


🔁 展平所有文章的作者对

Function<Article, Stream<PairOfAuthors>> toPairOfAuthors =
    article -> article.authors().stream()
                      .flatMap(firstAuthor -> buildPairOfAuthors.apply(article, firstAuthor));

💡 我们对每篇文章的作者使用 flatMap(),构建所有可能的作者对(去除无效)。


📊 构建作者对出现频次的直方图

Map<PairOfAuthors, Long> numberOfAuthorsTogether =
    articles.stream()
            .flatMap(toPairOfAuthors)
            .collect(Collectors.groupingBy(
                Function.identity(),
                Collectors.counting()));

📦 用 groupingBy + counting() 聚合每对作者出现的次数。


🥇 找出合作最多的一对作者

Function<Map<PairOfAuthors, Long>, Map.Entry<PairOfAuthors, Long>> maxExtractor =
    map -> map.entrySet().stream()
              .max(Map.Entry.comparingByValue())
              .orElseThrow();

☂️ orElseThrow() 会在没有任何作者对时抛异常,所以要确保至少有一篇文章包含两个作者。


🧪 示例数据和完整运行逻辑

var maria = new Author("Maria");
var james = new Author("James");
var patricia = new Author("Patricia");
var michael = new Author("Michael");

var articles = List.of(
    new Article("About As You Like It", 2015, List.of(maria)),
    new Article("About King John", 2015, List.of(james)),
    new Article("About The Winter's Tale", 2016, List.of(patricia)),
    new Article("About Richard II", 2017, List.of(michael)),
    new Article("About Richard III", 2019, List.of(maria, patricia)),
    new Article("About Henry VIII", 20219, List.of(patricia, michael)),
    new Article("About Romeo and Juliet", 2020, List.of(maria, patricia, james)),
    new Article("About Macbeth", 2021, List.of(maria, james, michael)),
    new Article("About Hamlet", 2021, List.of(patricia, james, michael)),
    new Article("About King Lear", 2022, List.of(maria, james, patricia, michael))
);

Map.Entry<PairOfAuthors, Long> pair = maxExtractor.apply(numberOfAuthorsTogether);

System.out.println("The authors that published the most together are " +
    pair.getKey().first().name() + " and " + pair.getKey().second().name() +
    ", they wrote " + pair.getValue() + " articles together.");

🧾 输出结果:

The authors that published the most together are Patricia and Michael, they wrote 3 articles together.

💡 小结与思考

✅ 使用 Optional.stream() 可以优雅处理“可能不存在”的值 ✅ 用 flatMap() 组合嵌套结构(多个 List 变为流) ✅ groupingBy + counting 构建频次统计 ✅ max() + orElseThrow() 获取最大值时要注意异常安全性