如何组合包含相同类型的两个HashMap对象?

Mav*_*vin 229 java hashmap

我有两个HashMap对象定义如下:

HashMap<String, Integer> map1 = new HashMap<String, Integer>();
HashMap<String, Integer> map2 = new HashMap<String, Integer>();
Run Code Online (Sandbox Code Playgroud)

我还有第三个HashMap对象:

HashMap<String, Integer> map3;
Run Code Online (Sandbox Code Playgroud)

我该如何合并map1,并map2汇集成map3

a_h*_*ame 325

map3 = new HashMap<>();

map3.putAll(map1);
map3.putAll(map2);
Run Code Online (Sandbox Code Playgroud)

  • 请注意,使用此解决方案,如果两个映射中都存在键,则将保留map2中的值,并且map1中的值将丢失. (87认同)
  • 我不知道OPer的期望.也许他希望map1值具有优先级,或者抛出异常,或者对交叉的整数执行某些"合并"操作.或者,也许,因为这是一个初学者的问题,这是OPer没有考虑的情况,在这种情况下,我的评论希望是有帮助的. (35认同)
  • @MichaelScheper:你还有什么期望?根据定义,"地图"中的键是唯一的 (5认同)
  • 谢谢,我正在将地图合并到 for 循环中,该循环使用返回地图的方法并需要将其合并到另一个地图并再次应用相同的方法。为此,我使用 putAll 方法得到空指针异常。它无助于使用 try/catch 块。我该怎么办?如果条件,如果 size==o 则不应用 putAll else 应用它等等.... (2认同)
  • 如果你得到一个 NPE,那么显然你没有正确初始化你的对象之一。您是否在 catch 块中打印堆栈跟踪?这样您就知道问题出在“哪里”。但是,除非您发布完整且准确的代码(包括堆栈跟踪),否则您将需要自己进行跟踪。 (2认同)

Jef*_*oom 102

如果您知道没有重复键,或者您希望值map2覆盖map1重复键的值,则可以编写

map3 = new HashMap<>(map1);
map3.putAll(map2);
Run Code Online (Sandbox Code Playgroud)

如果您需要更多地控制值的组合方式,可以使用Map.mergeJava 8中添加的,它使用用户提供的方法BiFunction来合并重复键的值. merge对单个键和值进行操作,因此您需要使用循环或Map.forEach.这里我们连接重复键的字符串:

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));
Run Code Online (Sandbox Code Playgroud)

如果您知道自己没有重复的密钥并希望强制执行它,则可以使用抛出以下内容的合并函数AssertionError:

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

从这个特定问题退一步,Java 8流库提供toMapgroupingBy 收集器.如果您在循环中反复合并映射,则可以重构计算以使用流,这既可以澄清您的代码,又可以使用并行流和并发收集器实现轻松并行.


Vit*_*nko 45

使用Java 8 Stream API的单线程:

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

此方法的好处之一是能够传递合并函数,该函数将处理具有相同键的值,例如:

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

  • @ArpitJ。这就是第二个变体的重点。有时你想要例外,有时你不想要。 (5认同)
  • 这将为重复键抛出 IllegalStateException (3认同)

Vad*_*zim 34

用于合并两个映射的Java 8替代单行程序:

defaultMap.forEach((k, v) -> destMap.putIfAbsent(k, v));
Run Code Online (Sandbox Code Playgroud)

与方法参考相同:

defaultMap.forEach(destMap::putIfAbsent);
Run Code Online (Sandbox Code Playgroud)

或者使用第三张地图的原始地图解决方案的idemponent:

Map<String, Integer> map3 = new HashMap<String, Integer>(map2);
map1.forEach(map3::putIfAbsent);
Run Code Online (Sandbox Code Playgroud)

这里有一种方法可以将两个映射合并到具有最少可能的中间复制操作的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();
Run Code Online (Sandbox Code Playgroud)

有关两个映射中存在的值需要与映射函数组合的情况,另请参阅使用Java 8合并两个映射.


Xae*_*ess 31

如果您不需要为可变性最终的地图,还有番石榴的 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();
}
Run Code Online (Sandbox Code Playgroud)

当然,这个方法可以更通用,使用varargs和循环putAll Maps来自参数等但我想展示一个概念.

此外,ImmutableMapBuilder有几个限制(或可能是功能?):

  • 它们是null hostile(throw NullPointerException- 如果map中的任何键或值为null)
  • Builder不接受重复键(IllegalArgumentException如果添加了重复键,则抛出).


fas*_*ava 21

你可以使用Collection.addAll()对于其他类型,例如List,Set等等.Map你可以使用putAll.


Zhe*_*lov 11

用于组合两个可能共享公共密钥的映射的通用解决方案:

到位:

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));
}
Run Code Online (Sandbox Code Playgroud)

返回新地图:

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;
}
Run Code Online (Sandbox Code Playgroud)


Ish*_*att 6

很晚了,但让我分享我在遇到同样问题时所做的事情。

Map<String, List<String>> map1 = new HashMap<>();
map1.put("India", Arrays.asList("Virat", "Mahi", "Rohit"));
map1.put("NZ", Arrays.asList("P1","P2","P3"));

Map<String, List<String>> map2 = new HashMap<>();
map2.put("India", Arrays.asList("Virat", "Mahi", "Rohit"));
map2.put("NZ", Arrays.asList("P1","P2","P4"));

Map<String, List<String>> collect4 = Stream.of(map1, map2)
                .flatMap(map -> map.entrySet().stream())
                .collect(
                        Collectors.toMap(
                                Map.Entry::getKey,
                                Map.Entry::getValue,
                                (strings, strings2) -> {
                                    List<String> newList = new ArrayList<>();
                                    newList.addAll(strings);
                                    newList.addAll(strings2);
                                    return newList;
                                }
                        )
                );
collect4.forEach((s, strings) -> System.out.println(s+"->"+strings));
Run Code Online (Sandbox Code Playgroud)

它给出了以下输出

NZ->[P1, P2, P3, P1, P2, P4]
India->[Virat, Mahi, Rohit, Virat, Mahi, Rohit]
Run Code Online (Sandbox Code Playgroud)