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

J’ai deux objects HashMap définis comme suit:

 HashMap map1 = new HashMap(); HashMap map2 = new HashMap(); 

J’ai aussi un troisième object HashMap :

 HashMap map3; 

Comment puis-je fusionner map1 et map2 ensemble dans map3 ?

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

Si vous savez que vous n’avez pas de clés en double ou que vous voulez que les valeurs de map2 remplacent les valeurs de map1 pour les clé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 une fonction BiFunction fournie par l’ BiFunction pour fusionner les valeurs des clés en double. merge opère 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 des clés en double:

 map3 = new HashMap<>(map1); for (Map.Entry e : map2.entrySet()) map3.merge(e.getKey(), e.getValue(), Ssortingng::concat); //or instead of the above loop map2.forEach((k, v) -> map3.merge(k, v, Ssortingng::concat)); 

Si vous savez que vous ne disposez pas de clés en double et que vous souhaitez les 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 stream Java 8 fournit toMap et groupingBy Collectors . Si vous fusionnez à plusieurs resockets des cartes dans une boucle, vous pouvez restructurer votre calcul pour utiliser des stream, ce qui peut à la fois clarifier votre code et permettre un parallélisme facile à l’aide d’un stream parallèle et d’un collecteur simultané.

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, citons la possibilité de passer une fonction de fusion, qui traitera des 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)) 

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

Exemple d’utilisation:

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

Bien sûr, cette méthode peut être plus générique, utiliser varargs et loop pour mettre putAll Maps partir d’arguments etc. mais je voulais montrer un concept.

En outre, ImmutableMap et son Builder ont peu de limitations (ou peut-être de fonctionnalités?):

  • ils sont null hostile (jetez NullPointerException – si une clé ou une valeur de map est null)
  • Le constructeur n’accepte pas les clés en double (lève IllegalArgumentException si des clés en double ont été ajoutées).

Java 8 alternative à une ligne pour fusionner deux cartes:

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

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

 defaultMap.forEach(destMap::putIfAbsent); 

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

 Map map3 = new HashMap(map2); map1.forEach(map3::putIfAbsent); 

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

 ImmutableMap.Builder builder = ImmutableMap.builder(); builder.putAll(map1); map2.forEach((k, v) -> {if (!map1.containsKey(k)) builder.put(k, v);}); ImmutableMap 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.

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

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

En place:

 public static  void mergeInPlace(Map map1, Map map2, BinaryOperator combiner) { map2.forEach((k, v) -> map1.merge(k, v, combiner::apply)); } 

Retourner une nouvelle carte:

 public static  Map merge(Map map1, Map map2, BinaryOperator combiner) { Map map3 = new HashMap<>(map1); map2.forEach((k, v) -> map3.merge(k, v, combiner::apply)); return map3; } 

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 une clé identique – alors elle remplacera la valeur de la clé de la première carte de hachage par la valeur de la clé de la seconde carte de hachage.

Pour être plus sûr – changez les valeurs de clé – vous pouvez utiliser le préfixe ou le suffixe sur les touches – (préfixe / suffixe différent pour la première carte de hachage et préfixe / suffixe différent pour la seconde carte de hachage)