Java中如何在HashMap的值为ArrayList时添加元素
[LOADING...]
1. 概述
在本教程中,我们将探讨如何在 Java 中使用 Map 为单个键存储多个值。一种实现方式是将 List 作为 Map 的值类型,即使用 Map<K, List<V>> 这种数据结构。
我们将演示如何使用 HashMap 和 ArrayList,当然你也可以根据需要使用其他 Map 或 List 的实现。此外,我们还可以使用支持多值数据结构的第三方库。本文将介绍实现此需求的三种方法,包括如何安全地向同一个键添加多个值。
2. 手动处理键、值和列表元素
在第一种方法中,我们将一切逻辑交由自己处理,并手动添加键值对:
我们手动检查 HashMap 是否包含我们尝试添加的 key,以确保有一个有效的 ArrayList 来存储值。 如果该键没有对应的有效 List,我们将使用 put 方法将其初始化,即 map.put(key, new ArrayList<>())。
一旦确定映射中存在该 key 对应的有效 List,我们就可以手动向 List 添加值。 我们可以通过 get 方法获取该 List,即 map.get(key),然后使用 List 的 add 方法添加 value,最终实现为 map.get(key).add(value)。
我们可以直接在空的 HashMap 上使用此方法:
这将构建一个形如 {key1=[key1_value1, key1_value2], key2=[key2_value1]} 的 Map。由此可见,并非每个 List 都必须包含相同数量的值。
这个方案虽然略显冗长,但非常通用,适用于任何 Java 版本(甚至 Java 5),并且有助于理解此实现的基本逻辑。
然而,该版本在计算效率上不如后续的解决方案。 这里我们执行了两次或三次查找:containsKey 和 get 方法各执行一次查找,如果执行了 put 操作,则会产生第三次查找。我们可以通过空值检查来优化代码,避免同时调用 containsKey 和 get。
3. 利用 computeIfAbsent 方法
从 Java 8 开始,我们可以使用 Map 接口的 computeIfAbsent 方法。我们可以使用 computeIfAbsent 通过传入 key 来获取 Map 中的值;如果该 key 不存在,则会通过映射函数计算出一个新值并将其插入到 Map 中:
通过使用 computeIfAbsent,我们无需手动执行 containsKey 检查,因为初始化过程已自动完成。
同样,我们可以构建与之前相同的 Map:
此代码的主要优点是利用 computeIfAbsent 自动化并优化了之前需要手动完成的操作。 computeIfAbsent 只需一次查找,而之前的版本需要两到三次,因为它在定位到内存地址后直接决定后续操作。不过,使用 computeIfAbsent 要求 Java 版本高于 Java 8。
4. 使用第三方库
在 HashMap 中为单个键存储多个值是一个常见问题。因此,我们可以依赖第三方库提供的数据结构来解决。我们将介绍三个最流行的库,以便你根据项目中已有的依赖进行选择。
4.1. Apache Commons Collections 的 MultiValuedMap
Apache Commons Collections 库提供了 MultiValuedMap 接口,允许我们为一个键存储多个值:
这看起来与前面的方法类似,但我们不需要手动管理 ArrayList,只需调用 put 方法,它会自动处理一切。 此外,它还提供了许多其他辅助方法,方便我们与 Map 内容进行交互。
4.2. Spring 的 LinkedMultiValueMap
另一个支持此类 Map 的流行库是 Spring。它提供了 LinkedMultiValueMap 接口来实现我们的目标:
可以看到,map 类型声明为两个 String,即 MultiValueMap<String, String>。 因为它是一个 LinkedMultiValueMap,会自动处理相同键的值。而在之前的方法中,我们使用的是一个 HashMap,其键为 String,值为对应的 List<String>。
4.3. Google Guava 的 Multimap
最后,Google Guava 库也提供了 Multimap 接口来实现键对应多个值的 Map:
可以看出,所有第三方库的语法都非常相似。Guava 库效率极高,被广泛应用于许多大型项目中。如果你的代码库中已经引入了 Guava,那么直接使用它将非常方便。
5. 总结
在本文中,我们讨论了如何在类 Map 结构中为一个键存储多个值。 我们介绍了三种解决方案,你可以根据代码环境和需求进行选择。
第一种方案详细介绍了每一步过程,手动处理 HashMap 中每个键对应的 ArrayList。它兼容所有 Java 版本,但略显冗长,且可能存在一些不必要的计算开销。
第二种方案利用了 Java 8 的新方法,依然使用 Map 配合 List,但通过接口方法自动化了检查逻辑。
最后,第三种方案探讨了第三方库提供的接口,即 Apache Commons Collections 的 MultiValuedMap、Spring 的 LinkedMultiValueMap 以及 Google Guava 的 Multimap。如果你的代码库中已经使用了这些库,利用它们提供的结构可以显著简化代码。