317. Java Stream API - 使用 groupingBy() 构建直方图并提取最大值

0 阅读2分钟

317. Java Stream API - 使用 groupingBy() 构建直方图并提取最大值

✅ Part 1:构建直方图(Histogram)

我们有一个字符串列表,想要统计每种字符串长度的出现次数。这个任务可以通过 Collectors.groupingBy 配合 Collectors.counting() 来完成:

Collection<String> strings = List.of(
    "one", "two", "three", "four", "five", "six", "seven", "eight", "nine",
    "ten", "eleven", "twelve"
);

Map<Integer, Long> histogram = strings.stream()
    .collect(Collectors.groupingBy(String::length, Collectors.counting()));

histogram.forEach((length, count) -> System.out.println(length + " :: " + count));

🖨 输出:

3 :: 4
4 :: 3
5 :: 3
6 :: 2

🔍 说明:

  • String::length 是分组键:字符串的长度。
  • Collectors.counting() 是统计该组中元素的数量。
  • 最终结果是一个直方图:键是字符串长度,值是该长度出现的次数。

✅ Part 2:提取最大值(提取出现次数最多的字符串长度)

📌 方法一:用 Map.Entry 直接找最大值

我们想要从直方图中找到数量最多的那组。例如上面例子中,3 :: 4 是数量最多的(长度为3的字符串有4个)。

Map.Entry<Integer, Long> maxValue = histogram.entrySet().stream()
    .max(Map.Entry.comparingByValue())
    .orElseThrow();

System.out.println("maxValue = " + maxValue);

🖨 输出:

maxValue = 3=4

📌 小技巧:

  • entrySet().stream() 是将 Map 转换为流。
  • Map.Entry.comparingByValue() 是基于值的比较器。
  • orElseThrow() 是保险措施,避免空结果。

⚠️ Part 3:多个最大值时如何处理?

如果出现多个 key 拥有相同的最大 value,那么上面方法只能选出一个,其他会被忽略。例如:

Collection<String> strings = List.of(
    "two", "three", "four", "five", "six", "seven", "eight", "nine",
    "ten", "eleven", "twelve"
);

输出的直方图:

3 :: 3
4 :: 3
5 :: 3
6 :: 2

👀 问题:有三个长度(3、4、5)的字符串数量都是 3。

如果你用 .max(),只会返回其中之一,可能会遗漏其他候选最大值。


✅ Part 4:如何提取所有最大值(Map 反转技巧)

🎯 思路: 我们可以将直方图 反转,把 "数量" 作为 key,"长度" 作为 value(或 List of value):

Map<Long, List<Integer>> reversed = histogram.entrySet().stream()
    .collect(Collectors.groupingBy(
        Map.Entry::getValue,                    // 用 count 作为 key
        Collectors.mapping(Map.Entry::getKey,   // 收集 length
            Collectors.toList())
    ));

然后找出最大 key,对应的 List 就是所有最大值:

Long maxCount = reversed.keySet().stream()
    .max(Long::compare)
    .orElseThrow();

List<Integer> lengthsWithMaxCount = reversed.get(maxCount);
System.out.println("最大数量 = " + maxCount + ",对应长度 = " + lengthsWithMaxCount);

🖨 输出:

最大数量 = 3,对应长度 = [3, 4, 5]

✅ 总结

操作目标推荐方法
构建直方图(统计数量)groupingBy(..., counting())
提取唯一最大值(value 最大)entrySet().stream().max(comparingByValue())
提取多个最大值(存在并列最大)Map 反转 + groupingBy + max(keySet())
提升可读性使用 Java 16+ 的 record