Comment puis-je combiner deux objets HashMap contenant les mêmes types?

241

J'ai deux HashMapobjets définis comme ceci:

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

J'ai aussi un troisième HashMapobjet:

HashMap<String, Integer> map3;

Comment puis-je fusionner map1et map2ensemble map3?

Mavin
la source
16
Vous n'avez pas indiqué ce que vous voulez faire si une clé existe dans les deux cartes.
Michael Scheper

Réponses:

344
map3 = new HashMap<>();

map3.putAll(map1);
map3.putAll(map2);
un cheval sans nom
la source
1
merci, je fusionne les cartes dans une boucle for qui utilise une méthode renvoie une carte et doit la fusionner avec une autre carte et appliquer la même méthode agian. Pour cela, j'obtiens une exception de pointeur nul avec la méthode putAll. cela n'aide pas d'utiliser le bloc try / catch. que devrais-je faire? Je suis appliquer si condition, que si taille == o alors ne pas appliquer putAll else l'appliquer et ainsi de suite ....
Mavin
1
Si vous obtenez un NPE, vous n'avez apparemment pas initialisé correctement l'un de vos objets. Imprimez-vous le stacktrace dans le bloc catch? Vous savez donc d' où vient le problème. Mais à moins que vous ne publiez le code complet et exact, y compris la trace de la pile, vous devrez le retrouver par vous-même.
a_horse_with_no_name
95
Notez que, avec cette solution, si une clé existe dans les deux cartes, la valeur dans map2 sera préservée et la valeur dans map1 perdue.
Michael Scheper
5
@MichaelScheper: qu'attendez-vous d'autre? Les clés dans un Mapsont par définition uniques
a_horse_with_no_name
42
Je ne sais pas ce que l'OPer attend. Peut-être qu'il s'attend à ce que les valeurs de map1 aient la priorité, ou qu'une exception soit levée, ou qu'une opération de `` fusion '' soit effectuée sur des entiers entrecroisés. Ou peut-être, puisque c'est une question pour débutant, c'est un cas que l'OPer n'avait pas considéré, auquel cas mon commentaire serait, je l'espère, utile.
Michael Scheper
109

Si vous savez que vous n'avez pas de clés en double ou que vous souhaitez que les valeurs map2écrasent les valeurs des map1clés en double, vous pouvez simplement écrire

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

Si vous avez besoin de plus de contrôle sur la façon dont les valeurs sont combinées, vous pouvez utiliser Map.merge, ajouté dans Java 8, qui utilise un fourni par l'utilisateur BiFunctionpour fusionner les valeurs des clés en double. mergefonctionne sur des clés et des valeurs individuelles, vous devrez donc utiliser une boucle ou Map.forEach. Ici, nous concaténons des chaînes pour les clés en double:

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

Si vous savez que vous n'avez pas de clés en double et que vous souhaitez les appliquer, vous pouvez utiliser une fonction de fusion qui lance un AssertionError:

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

Prendre un pas en arrière de cette question spécifique, les flux de Java 8 bibliothèque offre toMapet groupingBy collectionneurs . Si vous fusionnez à plusieurs reprises des cartes dans une boucle, vous pourrez peut-être restructurer votre calcul pour utiliser des flux, ce qui peut à la fois clarifier votre code et permettre un parallélisme facile en utilisant un flux parallèle et un collecteur simultané.

Jeffrey Bosboom
la source
46

One-liner utilisant Java 8 Stream API:

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

Parmi les avantages de cette méthode, il y a la possibilité de passer une fonction de fusion, qui traitera des valeurs qui ont la même clé, par exemple:

map3 = Stream.of(map1, map2).flatMap(m -> m.entrySet().stream())
       .collect(Collectors.toMap(Entry::getKey, Entry::getValue, Math::max))
Vitalii Fedorenko
la source
1
cela lèvera IllegalStateException pour les clés en double
Arpit J.
1
@ArpitJ. c'est tout l'intérêt de la deuxième variation. Parfois, vous voulez l'exception, parfois non.
Alex R
36

Java 8 alternatif one-liner pour fusionner deux cartes:

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

La même chose avec la référence de méthode:

defaultMap.forEach(destMap::putIfAbsent);

Ou idemponent pour une solution de cartes originale avec une troisième carte:

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

Et voici un moyen de fusionner deux cartes en une carte immuable rapide avec Guava qui effectue le moins d'opérations de copie intermédiaire possibles:

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

Voir aussi Fusionner deux cartes avec Java 8 pour les cas où les valeurs présentes dans les deux cartes doivent être combinées avec la fonction de cartographie.

Vadzim
la source
32

Si vous n'avez pas besoin de mutabilité pour votre carte finale, il y a celle de Guava ImmutableMap avec sa méthodeBuilder et qui, contrairement à la méthode d'interface de Java , peut être enchaînée.putAllMap

Exemple d'utilisation:

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

Bien sûr, cette méthode peut être plus générique, utiliser des varargs et des boucles à putAll Mapspartir d'arguments, etc. mais je voulais montrer un concept.

De plus, ImmutableMapet ses Builderlimites sont limitées (ou peut-être des fonctionnalités?):

  • ils sont nuls hostiles (throw NullPointerException- si une clé ou une valeur dans la carte est nulle)
  • Le générateur n'accepte pas les clés en double (lève IllegalArgumentExceptionsi des clés en double ont été ajoutées).
Xaerxess
la source
21

Vous pouvez utiliser Collection.addAll () pour d' autres types, par exemple List, Setetc. Pour Map, vous pouvez utiliser putAll.

fastcodejava
la source
11

Solution générique pour combiner deux cartes pouvant éventuellement partager des clés communes:

En place:

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

Retour d'une nouvelle carte:

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;
}
ZhekaKozlov
la source
2

Un petit extrait que j'utilise très souvent pour créer une carte à partir d'autres cartes:

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;
}
Thomas Decaux
la source
2

vous pouvez utiliser HashMap<String, List<Integer>>pour fusionner les deux hashmaps et éviter de perdre des éléments associés à la même clé.

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

production:

{key1: [1, 4]}
{key2: [2, 5]}
{key3: [3, 6]}
Omer Vishlitzky
la source
2

Très tard mais permettez-moi de partager ce que j'ai fait quand j'ai eu le même problème.

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

Il donne la sortie suivante

NZ->[P1, P2, P3, P1, P2, P4]
India->[Virat, Mahi, Rohit, Virat, Mahi, Rohit]
Ishan Bhatt
la source
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);

Les éléments en double ne seront pas ajoutés (c'est-à-dire les clés en double) car lorsque nous imprimerons hs3, nous n'obtiendrons qu'une seule valeur pour la clé 5 qui sera la dernière valeur ajoutée et ce sera rat. ** [L'ensemble a la propriété de ne pas autoriser la clé en double mais les valeurs peuvent être en double]

Karunesh
la source
0

Méthode 1: mettre des cartes dans une liste, puis rejoindre

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


}

Méthode 2: fusion normale de carte

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


}
Soudipta Dutta
la source
0

Vous pouvez utiliser la fonction putAll pour Map comme expliqué dans le code ci-dessous

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);
P Mittal
la source
0

L'extrait ci-dessous prend plus d'une carte et les combine.

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

Exemple de lien de démonstration .

Hari Krishna
la source
-1

vous pouvez utiliser - méthode addAll

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

Mais il y a toujours ce problème qui - si vos deux cartes de hachage ont la même clé - alors il remplacera la valeur de la clé de la première carte de hachage par la valeur de la clé de la deuxième carte de hachage.

Pour être plus sûr - modifiez les valeurs de clé - vous pouvez utiliser un préfixe ou un suffixe sur les clés - (préfixe / suffixe différent pour la première carte de hachage et préfixe / suffixe différent pour la deuxième carte de hachage)

Ashish Shetkar
la source