
理解问题:收集所有最大值键
在处理 Map
传统的 Stream API max(Map.Entry.comparingByValue()) 操作通常只会返回一个 Optional
方法一:利用 Java 8 Stream API 进行分组与聚合
Java 8 的 Stream API 提供了强大且富有表达力的方式来处理集合数据。通过结合 Collectors.groupingBy 和 Collectors.mapping,我们可以优雅地解决此问题。
核心思想
这种方法的核心在于将原始的 Map
立即学习“Java免费学习笔记(深入)”;
实现步骤
-
创建 Entry Stream:从原始 Map 中获取 entrySet() 并转换为 Stream
>。 -
按值分组:使用 Collectors.groupingBy(Map.Entry::getValue, Collectors.mapping(Map.Entry::getKey, Collectors.toList())) 将 Entry 流按其值(Integer)进行分组。groupingBy 的第一个参数 Map.Entry::getValue 指定了分组的依据,第二个参数 mapping(Map.Entry::getKey, toList()) 是一个下游收集器,它将每个分组中的 Map.Entry 的键(String)提取出来并收集到一个 List
中。这一步的结果是一个 Map >。 -
查找最大值分组:对新生成的 Map
> 的 entrySet() 再次创建 Stream。 -
获取最大键对应的列表:使用 max(Map.Entry.
>comparingByKey()) 找到这个 Stream 中键(即原始值)最大的 Entry。最后,通过 orElseThrow().getValue() 获取到这个最大键对应的 List 。
示例代码
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import static java.util.stream.Collectors.groupingBy;
import static java.util.stream.Collectors.mapping;
import static java.util.stream.Collectors.toList;
public class MaxKeysCollector {
public static void main(String[] args) {
final Map map = new HashMap<>();
map.put("first", 50);
map.put("second", 10);
map.put("third", 50);
map.put("fourth", 20);
map.put("fifth", 50);
List maxKeysStream = map.entrySet()
.stream()
// 1. 按值分组,将Map转换为Map>
.collect(groupingBy(Map.Entry::getValue, mapping(Map.Entry::getKey, toList())))
.entrySet()
.stream()
// 2. 找到新Map中键(即原始值)最大的Entry
.max(Map.Entry.>comparingByKey())
// 3. 取出最大值对应的键列表
.orElseThrow(() -> new IllegalStateException("Map cannot be empty to find max keys."))
.getValue();
System.out.println("Stream API 结果: " + maxKeysStream); // 预期输出: [first, third, fifth] (顺序可能不同)
}
} 注意事项
这种方法简洁且具有声明式风格,代码可读性强。然而,它涉及两次 Stream 迭代和一次中间 Map 的创建。对于包含大量数据的 Map,这可能会带来一定的性能开销,尽管第二次迭代的 Map 规模通常会小于原始 Map。
方法二:高效的传统单次迭代循环
对于性能要求极高的场景,或者当 Map 包含的数据量非常大时,传统的单次迭代 for 循环通常能提供最优的性能。
核心思想
该方法通过一次遍历 Map 的所有 Entry 来实现。在遍历过程中,我们维护一个当前已知的最大值 maxValue 和一个存储所有与 maxValue 关联的键的列表 maxKeys。
实现步骤
-
初始化:创建一个空的 List
用于存储最大值对应的键,并初始化一个 maxValue 变量为 Integer.MIN_VALUE(或 Map 中第一个 Entry 的值)。 - 遍历 Map:迭代 map.entrySet() 中的每一个 Entry。
-
比较与更新:
- 如果当前 Entry 的值 e.getValue() 小于 maxValue,则说明它不是最大值,直接跳过。
- 如果 e.getValue() 大于 maxValue,这表示我们找到了一个新的更大的最大值。此时,需要清空 maxKeys 列表,因为之前收集的键已不再是最大值对应的键。然后,更新 maxValue 为 e.getValue()。
- 最后,将当前 Entry 的键 e.getKey() 添加到 maxKeys 列表中。
示例代码
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class MaxKeysCollector {
public static void main(String[] args) {
final Map map = new HashMap<>();
map.put("first", 50);
map.put("second", 10);
map.put("third", 50);
map.put("fourth", 20);
map.put("fifth", 50);
List maxKeysLoop = new ArrayList<>();
int maxValue = Integer.MIN_VALUE; // 初始化为最小整数值
// 检查Map是否为空,避免在空Map上操作
if (map.isEmpty()) {
System.out.println("Map is empty, no max keys.");
return;
}
for (Map.Entry e : map.entrySet()) {
if (e.getValue() < maxValue) {
// 当前值小于已知最大值,跳过
continue;
}
if (e.getValue() > maxValue) {
// 发现新的更大的最大值,清空旧的maxKeys
maxKeysLoop.clear();
maxValue = e.getValue(); // 更新最大值
}
// 当前值等于或大于最大值,添加到maxKeys
maxKeysLoop.add(e.getKey());
}
System.out.println("传统循环结果: " + maxKeysLoop); // 预期输出: [first, third, fifth] (顺序可能不同)
}
} 性能分析
这种方法只进行了一次 Map 遍历,因此在处理大量数据时,其性能通常优于 Stream API 的分组聚合方案。它避免了创建中间 Map 的开销,内存使用也更为高效。
选择合适的解决方案
-
Stream API 方案:
- 优点:代码简洁,声明式风格,可读性高,更符合 Java 8+ 的函数式编程范式。
- 缺点:可能涉及多次迭代和中间数据结构创建,对于超大型 Map 性能可能略逊。
- 适用场景:对代码优雅性和可读性要求较高,或 Map 数据量适中时。
-
传统单次迭代循环方案:
- 优点:性能最优,只进行一次迭代,内存开销小。
- 缺点:代码相对命令式,不如 Stream API 简洁。
- 适用场景:对性能有严格要求,或处理超大型 Map 时。
总结
本文介绍了两种在 Java 8 中从 Map










