web-dev-qa-db-fra.com

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

J'ai deux objets HashMap définis comme suit:

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

J'ai aussi un troisième objet HashMap:

HashMap<String, Integer> map3;

Comment fusionner map1 et map2 ensemble dans map3?

206
Mavin
map3 = new HashMap<>();

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

Si vous savez que vous n'avez pas de clés dupliquées ou si vous souhaitez que les valeurs de map2 écrasent les valeurs de map1 pour les clés dupliquées, vous pouvez simplement écrire:

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

Si vous avez besoin de plus de contrôle sur la combinaison des valeurs, vous pouvez utiliser Map.merge , ajouté à Java 8, qui utilise une variable BiFunction fournie par l'utilisateur pour fusionner les valeurs des clés en double. merge fonctionne sur des clés et des valeurs individuelles. Vous devez donc utiliser une boucle ou Map.forEach. Ici, nous concaténons des chaînes pour les clés dupliquées:

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 dupliquées et que vous souhaitez l'appliquer, vous pouvez utiliser une fonction de fusion qui génère une AssertionError:

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

En prenant du recul par rapport à cette question spécifique, la bibliothèque de flux Java 8 fournit toMap et groupingByCollectors . Si vous fusionnez de manière répétée des cartes dans une boucle, vous pourrez peut-être restructurer votre calcul pour utiliser des flux, ce qui clarifiera votre code et permettra un parallélisme aisé à l'aide d'un flux parallèle et d'un collecteur simultané.

92
Jeffrey Bosboom

One-Liner utilisant l'API Java 8 Stream:

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

Parmi les avantages de cette méthode, citons la possibilité de passer une fonction de fusion, qui traitera les valeurs ayant 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))
40
Vitalii Fedorenko

Java 8 alternative one-liner pour la fusion de deux cartes:

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

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

defaultMap.forEach(destMap::putIfAbsent);

Ou idemponent pour la solution de cartes originale avec la 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 rapide et immuable avec Guava qui effectue le moins d'opérations de copie intermédiaires 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 mappage.

29
Vadzim

Si vous n'avez pas besoin de mutabilité pour votre carte finale, il existe la méthode de GuavaImmutableMap avec sa Builder et putAll qui, contrairement à la méthode d'interface Map de Java , peut être chaîné.

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, utilisez varargs et loop pour putAllMaps à partir d'arguments, etc., mais je voulais montrer un concept.

De plus, ImmutableMap et sa Builder ont peu de limitations (ou peut-être de fonctionnalités?):

  • ils sont nullement hostiles (jetez NullPointerException - si une clé ou une valeur de map est nulle)
  • Le constructeur n'accepte pas les clés en double (jette IllegalArgumentException si des clés en double ont été ajoutées).
26
Xaerxess
25
hvgotcodes

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

17
fastcodejava

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

Retourner 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;
}
11
ZhekaKozlov

Très tard, mais laissez-moi 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]
1
Ishan Bhatt

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

sortie:

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

Un petit extrait que j'utilise très souvent pour créer des cartes à 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;
}
1
Thomas Decaux

Si vous souhaitez conserver la troisième carte de sorte que les modifications apportées à l'une des entrées ne soient pas reflétées dans les autres cartes.

HashMap<String, Integer> map3 = new HashMap<String, Integer>();
map3.putAll(map1);
map3.putAll(map2);
0

Méthode 1: placez les cartes dans une liste, puis rejoignez

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 de carte normale

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
Soudipta Dutta
    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. ** [Le jeu a la propriété de ne pas autoriser la clé en double mais les valeurs peuvent être dupliquées]

0
Karunesh

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