我如何组合两个包含相同types的HashMap对象?

我有两个像这样定义的HashMap对象:

 HashMap<String, Integer> map1 = new HashMap<String, Integer>(); HashMap<String, Integer> map2 = new HashMap<String, Integer>(); 

我也有第三个HashMap对象:

 HashMap<String, Integer> map3; 

如何将map1map2合并到map3

 map3 = new HashMap<>(); map3.putAll(map1); map3.putAll(map2); 

如果你知道你没有重复的键,或者你希望map2中的值覆盖来自map1的重复键的值,你可以写

 map3 = new HashMap<>(map1); map3.putAll(map2); 

如果需要更多控制值的组合方式,可以使用Java 8中添加的Map.merge ,它使用用户提供的BiFunction来合并重复键的值。 merge操作的个人键和值,所以你需要使用循环或Map.forEach 。 在这里,我们连接重复键的string:

 map3 = new HashMap<>(map1); for (Map.Entry<String, String> e : map2.entrySet()) map3.merge(e.getKey(), e.getValue(), String::concat); //or instead of the above loop map2.forEach((k, v) -> map3.merge(k, v, String::concat)); 

如果你知道你没有重复的键并且想强制它,你可以使用一个引发AssertionError的合并函数:

 map2.forEach((k, v) -> map3.merge(k, v, (v1, v2) -> {throw new AssertionError("duplicate values for key: "+k);})); 

从这个特定的问题退后一步,Java 8stream库提供了toMapgroupingBy Collector 。 如果你在一个循环中重复合并地图,你可以重构你的计算来使用stream,这可以澄清你的代码,并且使用并行stream和并发采集器来实现简单的并行。

如果你的最终地图不需要可变性,那么就有Guava的 ImmutableMap ,它的BuilderputAll方法 ,和Java的Map接口方法相比 ,可以被链接在一起。

使用示例:

 Map<String, Integer> mergeMyTwoMaps(Map<String, Integer> map1, Map<String, Integer> map2) { return ImmutableMap.<String, Integer>builder() .putAll(map1) .putAll(map2) .build(); } 

当然,这种方法可以更通用,使用可变参数和循环来从参数中putAll Maps等,但是我想展示一个概念。

另外, ImmutableMap和它的Builder没有什么限制(或者是function?):

  • 它们是空的敌对(抛出NullPointerException – 如果映射中的任何键或值为空)
  • 生成器不接受重复键(如果添加了重复键,则抛出IllegalArgumentException )。

使用Java 8 Stream API的单线程:

 map3 = Stream.of(map1, map2).flatMap(m -> m.entrySet().stream()) .collect(Collectors.toMap(Entry::getKey, Entry::getValue)) 

这种方法的好处是能够传递一个合并函数,它将处理具有相同密钥的值,例如:

 map3 = Stream.of(map1, map2).flatMap(m -> m.entrySet().stream()) .collect(Collectors.toMap(Entry::getKey, Entry::getValue, Math::max)) 

您可以使用Collection.addAll()作为其他types,例如ListSet等。对于Map ,您可以使用putAll

Java 8的另一种方法是合并两个地图:

 defaultMap.forEach((k, v) -> destMap.putIfAbsent(k, v)); 

或第三张地图的原始地图解决schemeidemponent:

 Map<String, Integer> map3 = new HashMap<String, Integer>(map2); map1.forEach((k, v) -> map3.putIfAbsent(k, v)); 

这里有一种方法可以将两个地图合并成一个Guava快速不可变的地图,而这个Guava的中间复制操作的可能性最小。

 ImmutableMap.Builder<String, Integer> builder = ImmutableMap.<String, Integer>builder(); builder.putAll(map1); map2.forEach((k, v) -> {if (!map1.containsKey(k)) builder.put(k, v);}); ImmutableMap<String, Integer> map3 = builder.build(); 

将两个可能共享公共密钥的地图组合在一起的通用解决scheme:

到位:

 public static <K, V> void mergeInPlace(Map<K, V> map1, Map<K, V> map2, BinaryOperator<V> combiner) { map2.forEach((k, v) -> map1.merge(k, v, combiner::apply)); } 

返回新地图:

 public static <K, V> Map<K, V> merge(Map<K, V> map1, Map<K, V> map2, BinaryOperator<V> combiner) { Map<K, V> map3 = new HashMap<>(map1); map2.forEach((k, v) -> map3.merge(k, v, combiner::apply)); return map3; } 

你可以使用–addAll方法

http://download.oracle.com/javase/6/docs/api/java/util/HashMap.html

但总是有这个问题 – 如果你的两个哈希映射有任何相同的键 – 那么它将覆盖第一个哈希映射的密钥值和第二个哈希映射的密钥值。

为了更安全一些 – 更改关键值 – 可以在键上使用前缀或后缀 – (第一个哈希映射使用不同的前缀/后缀,第二个哈希映射使用不同的前缀/后缀)