1. 概述
在这个教程中,我们将探讨如何将一个HashMap
的内容复制到另一个HashMap
中,而不替换目标HashMap
的键值对。在Java中,HashMap
是Map
接口的哈希表实现,用于存储键值对的数据结构。
2. 问题描述
假设我们有两个HashMap
,sourceMap
和targetMap
,它们包含国家及其首都作为键值对。我们想要将sourceMap
的内容复制到targetMap
中,这样我们就只保留一个包含所有国家及其首都的映射。复制需要遵循以下规则:
- 我们应该保留
targetMap
的原始内容 - 如果键存在冲突(例如两个地图中都有某个城市),我们应该保留
targetMap
的条目
以以下输入为例:
Map<String, String> sourceMap = new HashMap<>();
sourceMap.put("India", "Delhi");
sourceMap.put("United States", "Washington D.C.");
sourceMap.put("United Kingdom", "London D.C.");
Map<String, String> targetMap = new HashMap<>();
targetMap.put("Zimbabwe", "Harare");
targetMap.put("Norway", "Oslo");
targetMap.put("United Kingdom", "London");
修改后的targetMap
保留其值,并添加了sourceMap
的所有值:
"India", "Delhi"
"United States", "Washington D.C."
"United Kingdom", "London"
"Zimbabwe", "Harare"
"Norway", "Oslo"
3. 遍历HashMap
解决这个问题的一个简单方法是遍历sourceMap
的每个条目(键值对),并与targetMap
中的进行比较。当我们找到仅存在于sourceMap
中的条目时,将其添加到targetMap
中。最终的targetMap
包含了它本身和sourceMap
的所有键值。
我们可以跳过遍历两个地图的entrySets()
,而是在遍历sourceMap
的entrySet()
时检查键在targetMap
中的存在:
Map<String, String> copyByIteration(Map<String, String> sourceMap, Map<String, String> targetMap) {
for (Map.Entry<String, String> entry : sourceMap.entrySet()) {
if (!targetMap.containsKey(entry.getKey())) {
targetMap.put(entry.getKey(), entry.getValue());
}
}
return targetMap;
}
4. 使用Map
的putIfAbsent()
我们可以重构上述代码,使用Java 8中新增的putIfAbsent()
方法。这个方法的名称表明,如果指定条目中的键在目标HashMap
中不存在,才会复制sourceMap
的条目:
Map<String, String> copyUsingPutIfAbsent(Map<String, String> sourceMap, Map<String, String> targetMap) {
for (Map.Entry<String, String> entry : sourceMap.entrySet()) {
targetMap.putIfAbsent(entry.getKey(), entry.getValue());
}
return targetMap;
}
另一种不使用循环的方法是利用Java 8引入的forEach
构造。我们提供一个动作,即在给定HashMap
的每个条目上调用putIfAbsent()
方法,直到所有元素处理完毕或出现异常:
Map<String, String> copyUsingPutIfAbsentForEach(Map<String, String> sourceMap, Map<String, String> targetMap) {
sourceMap.forEach(targetMap::putIfAbsent);
return targetMap;
}
5. 使用Map
的putAll()
Map
接口提供了putAll()
方法,可以用来达到我们的目的。这个方法将输入映射的所有键值复制到当前映射中。需要注意的是,如果源和目标哈希映射的键存在冲突,源映射的条目将替换targetMap
的条目。
我们可以通过显式从sourceMap
中移除共同的键来解决这个问题:
Map<String, String> copyUsingPutAll(Map<String, String> sourceMap, Map<String, String> targetMap) {
sourceMap.keySet().removeAll(targetMap.keySet());
targetMap.putAll(sourceMap);
return targetMap;
}
6. 使用Maps.merge()
在Maps
上
Java 8在Maps
接口中引入了一个merge()
方法。它接受一个键、一个值和一个重映射功能作为参数。
如果在当前映射中指定的键与提供的非null
值关联,或者关联为null
,则方法将其关联到提供的非null
值。
如果键在两个映射中都存在,其关联的值将被给定重映射函数的结果替换。如果重映射函数的结果为null
,则删除键值对。
我们可以使用merge()
方法从sourceMap
复制条目到targetMap
:
Map<String, String> copyUsingMapMerge(Map<String, String> sourceMap, Map<String, String> targetMap) {
sourceMap.forEach((key, value) -> targetMap.merge(key, value, (oldVal, newVal) -> oldVal));
return targetMap;
}
我们的重映射函数确保在发生冲突时,保留targetMap
中的值。
7. 使用Guava的Maps.difference()
Guava库在其Maps
类中使用了一个difference()
方法。要使用Guava库,我们需要在pom.xml
中添加相应的依赖:
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>31.0.1-jre</version>
</dependency>
difference()
方法接受两个映射作为输入,计算这两个映射之间的差异。提供的映射的键应遵守equals()
和hashCode()
合同。
为了解决问题,我们首先计算映射之间的差异。一旦我们知道只存在于sourceMap
(左映射)中的条目,我们就可以将它们放入targetMap
:
Map<String, String> copyUsingGuavaMapDifference(Map<String, String> sourceMap, Map<String, String> targetMap) {
MapDifference<String, String> differenceMap = Maps.difference(sourceMap, targetMap);
targetMap.putAll(differenceMap.entriesOnlyOnLeft());
return targetMap;
}
8. 总结
在这篇文章中,我们探讨了如何在保留目标HashMap
现有条目的情况下,从一个HashMap
复制条目到另一个HashMap
。我们实现了基于迭代的方法,并使用不同的Java库函数解决了问题。我们也讨论了如何使用Guava库来解决这个问题。
如往常一样,所有的代码示例可以在GitHub上找到。