我如何组合两个包含相同types的HashMap对象?
我有两个像这样定义的HashMap
对象:
HashMap<String, Integer> map1 = new HashMap<String, Integer>(); HashMap<String, Integer> map2 = new HashMap<String, Integer>();
我也有第三个HashMap
对象:
HashMap<String, Integer> map3;
如何将map1
和map2
合并到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库提供了toMap
和groupingBy
Collector 。 如果你在一个循环中重复合并地图,你可以重构你的计算来使用stream,这可以澄清你的代码,并且使用并行stream和并发采集器来实现简单的并行。
HashMap有一个putAll
方法。
http://download.oracle.com/javase/6/docs/api/java/util/HashMap.html
如果你的最终地图不需要可变性,那么就有Guava的 ImmutableMap
,它的Builder
和putAll
方法 ,和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,例如List
, Set
等。对于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
但总是有这个问题 – 如果你的两个哈希映射有任何相同的键 – 那么它将覆盖第一个哈希映射的密钥值和第二个哈希映射的密钥值。
为了更安全一些 – 更改关键值 – 可以在键上使用前缀或后缀 – (第一个哈希映射使用不同的前缀/后缀,第二个哈希映射使用不同的前缀/后缀)