【问题标题】:How can I combine two HashMap objects containing the same types?如何组合包含相同类型的两个 HashMap 对象?
【发布时间】:2011-05-17 00:43:41
【问题描述】:

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

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

我还有第三个HashMap 对象:

HashMap<String, Integer> map3;

如何将map1map2 合并为map3

【问题讨论】:

    标签: java hashmap


    【解决方案1】:
    map3 = new HashMap<>();
    
    map3.putAll(map1);
    map3.putAll(map2);
    

    【讨论】:

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

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

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

    如果您需要更多地控制值的组合方式,可以使用 Java 8 中添加的 Map.merge,它使用用户提供的 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 Collectors。如果您在一个循环中反复合并映射,您可能能够重组您的计算以使用流,这既可以澄清您的代码,又可以使用并行流和并发收集器轻松实现并行。

    【讨论】:

      【解决方案3】:

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

      【讨论】:

      • 这将为重复键抛出 IllegalStateException
      • @ArpitJ。这就是第二个变体的重点。有时您想要例外,有时您不想要。
      • 我想知道为什么这个答案不那么受欢迎。
      【解决方案4】:

      用于合并两个地图的 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();
      

      另请参阅Merge two maps with Java 8,了解两个映射中存在的值需要与映射函数组合的情况。

      【讨论】:

        【解决方案5】:

        如果您的最终地图不需要可变性,可以使用Guava's ImmutableMap 及其BuilderputAll method,与Java's Map interface method 相比,它们可以链接起来。

        使用示例:

        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 几乎没有限制(或者可能是功能?):

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

        【讨论】:

          【解决方案6】:

          【讨论】:

          • 这个命令的运行时间是多少? doc似乎没有指定。我会假设 Theta(N)?
          【解决方案7】:

          您可以将Collection.addAll() 用于其他类型,例如ListSet等。Map可以使用putAll

          【讨论】:

            【解决方案8】:

            组合两个可能共享公共键的地图的通用解决方案:

            就地:

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

            【讨论】:

              【解决方案9】:

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

              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]
              

              【讨论】:

                【解决方案10】:

                一个我经常使用的小sn-p从其他地图创建地图:

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

                【讨论】:

                  【解决方案11】:

                  您可以使用HashMap&lt;String, List&lt;Integer&gt;&gt; 合并两个哈希图,避免丢失与相同键配对的元素。

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

                  【讨论】:

                    【解决方案12】:

                    您可以将 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);
                    

                    【讨论】:

                      【解决方案13】:

                      sn-p 下面需要多张地图并将它们组合起来。

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

                      演示示例link.

                      【讨论】:

                        【解决方案14】:

                        假设以下输入:

                            import java.util.stream.Stream;
                            import java.util.stream.Collectors;
                            import java.util.Map;
                           
                            ...
                        
                            var m1 = Map.of("k1", 1, "k2", 2);
                            var m2 = Map.of("k3", 3, "k4", 4);
                        

                        当您确定两个输入映射之间没有任何键冲突时,避免任何突变并产生不可变结果的简单表达式可能是:

                            var merged = Stream.concat(
                                m1.entrySet().stream(),
                                m2.entrySet().stream()
                            ).collect(Collectors.toUnmodifiableMap(Map.Entry::getKey, Map.Entry::getValue));
                        

                        如果可能发生键冲突,我们可以提供一个 lambda 来指定如何对它们进行重复数据删除。例如,如果我们想在两个输入中都存在条目的情况下保留最大值,我们可以:

                            .collect(Collectors.toUnmodifiableMap(
                                Map.Entry::getKey,
                                Map.Entry::getValue,
                                Math::max))  // any function  (Integer, Integer) -> Integer  is ok here
                        

                        【讨论】:

                          【解决方案15】:
                              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具有不允许重复键但值可以重复的属性]

                          【讨论】:

                            【解决方案16】:

                            方法一:将地图放入List,然后加入

                            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
                            
                            
                            }
                            

                            方法二:法线贴图合并

                            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
                            
                            
                            }
                                
                            

                            在 Python 中,HashMap 被称为 Dictionary,我们可以很容易地合并它们。

                            x = {'Roopa': 1, 'Tabu': 2}
                            y = {'Roopi': 3, 'Soudipta': 4}
                            
                            
                            z = {**x,**y}
                            print(z)
                            
                            {'Roopa': 1, 'Tabu': 2, 'Roopi': 3, 'Soudipta': 4}
                            

                            【讨论】:

                              【解决方案17】:

                              你可以使用-addAll方法

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

                              但总是存在这样的问题 - 如果您的两个哈希映射具有相同的任何键 - 那么它将用来自第二个哈希映射的键的值覆盖第一个哈希映射的键值。

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

                              【讨论】:

                                猜你喜欢
                                • 1970-01-01
                                • 1970-01-01
                                • 1970-01-01
                                • 1970-01-01
                                • 2020-10-03
                                • 2022-01-18
                                • 1970-01-01
                                • 1970-01-01
                                • 1970-01-01
                                相关资源
                                最近更新 更多