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


241

我有两个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


16
您还没有说明如果两个地图中都存在键,您想做什么。
Michael Scheper

Answers:


344
map3 = new HashMap<>();

map3.putAll(map1);
map3.putAll(map2);

1
谢谢,我将地图合并到for循环中,该循环使用一种方法返回地图,并且需要将其合并到另一张地图并应用相同的方法agian。为此,我使用putAll方法获取了空指针异常。它不利于使用try / catch块。我该怎么办?我是有条件的,如果大小== o,那么就不应用putAll,否则就应用它,依此类推....
Mavin

1
如果您获得了NPE,则显然您没有正确初始化一个对象。您是否在catch块中打印stacktrace?这样您就知道问题出在哪里。但是,除非您发布完整且准确的代码(包括堆栈跟踪),否则您将需要自行进行跟踪。
a_horse_with_no_name

95
请注意,使用此解决方案,如果两个映射中都存在键,则map2中的值将保留,而map1中的值将丢失。
Michael Scheper 2013年

5
@MichaelScheper:您还期望什么?Map根据定义,a 中的键是唯一的
a_horse_with_no_name 2013年

42
我不知道OPer期望什么。也许他希望map1值具有优先权,或者引发异常,或者希望对相交的Integer执行某些“合并”操作。或者,由于这是一个初学者的问题,因此OPer尚未考虑过这种情况,在这种情况下,我的评论有望有所帮助。
Michael Scheper 2013年

109

如果您知道没有重复的键,或者想让值map2覆盖map1重复键的值,则只需编写

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

如果需要更多控制值组合的方法,可以使用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));

如果您知道没有重复的键并想执行它,则可以使用抛出的合并功能AssertionError

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

从这个特定问题退后一步,Java 8流库提供了toMapgroupingBy 收集器。如果要在循环中重复合并地图,则可以重组计算以使用流,这样既可以澄清代码,又可以使用并行流和并发收集器实现轻松的并行性。


46

使用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))

1
这将为重复的键抛出IllegalStateException
Arpit J.

1
@ArpitJ。这就是第二个变化的重点。有时您想要例外,有时则不需要。
亚历克斯R

36

Java 8替代单行代码,用于合并两个地图:

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

与方法参考相同:

defaultMap.forEach(destMap::putIfAbsent);

或与第三张地图对应的原始地图解决方案:

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

这是一种使用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();

另请参阅将两个映射与Java 8合并,以了解两个映射中都存在的值需要与映射功能结合的情况。


32

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

当然,此方法可以更通用,可以使用varargs和putAll Maps从参数循环到循环等等,但是我想展示一个概念。

此外,ImmutableMap它的Builder局限性很小(也许还有功能?):

  • 它们是敌对的null(抛出NullPointerException-如果map中的任何键或值都为null)
  • Builder不接受重复的键(IllegalArgumentException如果添加了重复的键,则抛出该错误)。



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));
}

返回新地图:

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;
}

2

我经常使用的一小段代码从其他地图创建地图:

static public <K, V> Map<K, V> merge(Map<K, V>... args) {
    final Map<K, V> buffer = new HashMap<>();

    for (Map m : args) {
        buffer.putAll(m);
    }

    return buffer;
}

2

您可以HashMap<String, List<Integer>>用来合并两个哈希图,并避免丢失与同一键配对的元素。

HashMap<String, Integer> map1 = new HashMap<>();
HashMap<String, Integer> map2 = new HashMap<>();
map1.put("key1", 1);
map1.put("key2", 2);
map1.put("key3", 3);
map2.put("key1", 4);
map2.put("key2", 5);
map2.put("key3", 6);
HashMap<String, List<Integer>> map3 = new HashMap<>();
map1.forEach((str, num) -> map3.put(str, new ArrayList<>(Arrays.asList(num))));
//checking for each key if its already in the map, and if so, you just add the integer to the list paired with this key
for (Map.Entry<String, Integer> entry : map2.entrySet()) {
    Integer value = entry.getValue();
    String key = entry.getKey();
    if (map3.containsKey(key)) {
        map3.get(key).add(value);
    } else {
        map3.put(key, new ArrayList<>(Arrays.asList(value)));
    }
}
map3.forEach((str, list) -> System.out.println("{" + str + ": " + list + "}"));

输出:

{key1: [1, 4]}
{key2: [2, 5]}
{key3: [3, 6]}

2

很晚了,但是让我分享我遇到同样问题时的工作。

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));

它给出以下输出

NZ->[P1, P2, P3, P1, P2, P4]
India->[Virat, Mahi, Rohit, Virat, Mahi, Rohit]

0
    HashMap<Integer,String> hs1 = new HashMap<>();
    hs1.put(1,"ram");
    hs1.put(2,"sita");
    hs1.put(3,"laxman");
    hs1.put(4,"hanuman");
    hs1.put(5,"geeta");

    HashMap<Integer,String> hs2 = new HashMap<>();
    hs2.put(5,"rat");
    hs2.put(6,"lion");
    hs2.put(7,"tiger");
    hs2.put(8,"fish");
    hs2.put(9,"hen");

    HashMap<Integer,String> hs3 = new HashMap<>();//Map is which we add

    hs3.putAll(hs1);
    hs3.putAll(hs2);

    System.out.println(" hs1 : " + hs1);
    System.out.println(" hs2 : " + hs2);
    System.out.println(" hs3 : " + hs3);

重复的项将不会被添加(即重复的键),因为当我们打印hs3时,键5只会得到一个值,这是最后添加的值,并且会被分配。** [Set具有不允许重复的键,但值可以重复的属性]


0

方法1:将地图放入列表中,然后加入

public class Test15 {
public static void main(String[] args) {

    Map<String, List<String>> map1 = new HashMap<>();
    map1.put("London", Arrays.asList("A", "B", "C"));
    map1.put("Wales", Arrays.asList("P1", "P2", "P3"));

    Map<String, List<String>> map2 = new HashMap<>();
    map2.put("Calcutta", Arrays.asList("Protijayi", "Gina", "Gini"));
    map2.put("London", Arrays.asList( "P4", "P5", "P6"));
    map2.put("Wales", Arrays.asList( "P111", "P5555", "P677666"));

    System.out.println(map1);System.out.println(map2);



    // put the maps in an ArrayList

    List<Map<String, List<String>>> maplist = new ArrayList<Map<String,List<String>>>();
    maplist.add(map1);
    maplist.add(map2);
    /*
<T,K,U> Collector<T,?,Map<K,U>> toMap(

                                  Function<? super T,? extends K> keyMapper,

                                  Function<? super T,? extends U> valueMapper,

                                  BinaryOperator<U> mergeFunction)
    */

 Map<String, List<String>> collect = maplist.stream()
    .flatMap(ch -> ch.entrySet().stream())
    .collect(
            Collectors.toMap(

            //keyMapper,

            Entry::getKey,

            //valueMapper
            Entry::getValue,

            // mergeFunction
     (list_a,list_b) -> Stream.concat(list_a.stream(), list_b.stream()).collect(Collectors.toList())

            ));



    System.out.println("Final Result(Map after join) => " + collect);
    /*
    {Wales=[P1, P2, P3], London=[A, B, C]}
{Calcutta=[Protijayi, Gina, Gini], Wales=[P111, P5555, P677666], London=[P4, P5, P6]}
Final Result(Map after join) => {Calcutta=[Protijayi, Gina, Gini], Wales=[P1, P2, P3, P111, P5555, P677666], London=[A, B, C, P4, P5, P6]}
*/

}//main


}

方法2:法线贴图合并

public class Test15 {
public static void main(String[] args) {

    Map<String, List<String>> map1 = new HashMap<>();
    map1.put("London", Arrays.asList("A", "B", "C"));
    map1.put("Wales", Arrays.asList("P1", "P2", "P3"));

    Map<String, List<String>> map2 = new HashMap<>();
    map2.put("Calcutta", Arrays.asList("Protijayi", "Gina", "Gini"));
    map2.put("London", Arrays.asList( "P4", "P5", "P6"));
    map2.put("Wales", Arrays.asList( "P111", "P5555", "P677666"));

    System.out.println(map1);System.out.println(map2);




    /*
<T,K,U> Collector<T,?,Map<K,U>> toMap(

                                  Function<? super T,? extends K> keyMapper,

                                  Function<? super T,? extends U> valueMapper,

                                  BinaryOperator<U> mergeFunction)
    */


Map<String, List<String>> collect = Stream.of(map1,map2)
    .flatMap(ch -> ch.entrySet().stream())
    .collect(
            Collectors.toMap(

            //keyMapper,

            Entry::getKey,

            //valueMapper
            Entry::getValue,

            // mergeFunction
     (list_a,list_b) -> Stream.concat(list_a.stream(), list_b.stream()).collect(Collectors.toList())

            ));



    System.out.println("Final Result(Map after join) => " + collect);
    /*
    {Wales=[P1, P2, P3], London=[A, B, C]}
{Calcutta=[Protijayi, Gina, Gini], Wales=[P111, P5555, P677666], London=[P4, P5, P6]}
Final Result(Map after join) => {Calcutta=[Protijayi, Gina, Gini], Wales=[P1, P2, P3, P111, P5555, P677666], London=[A, B, C, P4, P5, P6]}

*/

}//main


}

0

您可以按照以下代码中的说明将putAll函数用于Map

HashMap<String, Integer> map1 = new HashMap<String, Integer>();
map1.put("a", 1);
map1.put("b", 2);
map1.put("c", 3);
HashMap<String, Integer> map2 = new HashMap<String, Integer>();
map1.put("aa", 11);
map1.put("bb", 12);
HashMap<String, Integer> map3 = new HashMap<String, Integer>();
map3.putAll(map1);
map3.putAll(map2);
map3.keySet().stream().forEach(System.out::println);
map3.values().stream().forEach(System.out::println);

0

下面的摘录包含一张以上的地图,并将其组合在一起。

 private static <K, V> Map<K, V> combineMaps(Map<K, V>... maps) {
        if (maps == null || maps.length == 0) {
            return Collections.EMPTY_MAP;
        }

        Map<K, V> result = new HashMap<>();

        for (Map<K, V> map : maps) {
            result.putAll(map);
        }
        return result;
    }

演示示例链接。


By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.