2010-11-28 28 views
171

J'ai deux objets HashMap définis comme ceci:Comment combiner deux objets HashMap contenant les mêmes types?

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 puis-je fusionner map1 et map2 ensemble dans map3?

+8

Vous n'avez pas indiqué ce que vous voulez faire si une clé existe dans les deux cartes. –

+5

Java 8 a map1.merge (map2) –

+4

@SambitTripathy, avez-vous un lien pour les documents sur ce sujet? Je n'arrive pas à le trouver. – JohnK

Répondre

16

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

+2

C'est pour les collections, pas pour Maps. –

+0

@Paul - La réponse est corrigée. – fastcodejava

234
map3 = new HashMap<>(); 

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

je vous remercie, je suis en train de fusionner les cartes dans une boucle for qui utilise une méthode de retour d'une carte et besoin de la fusionner à une autre carte et d'appliquer la même méthode agian. Pour cela, j'obtiens une exception de pointeur null avec la méthode putAll.cela n'aide pas à utiliser try/catch block. que devrais-je faire? Je suis appliquer si condition, que si size == o alors ne pas appliquer putAll else l'appliquer et ainsi de suite .... – Mavin

+1

Si vous obtenez un NPE, alors apparemment vous n'avez pas initialisé un de vos objets correctement. Imprimez-vous la pile dans le bloc catch? Donc, vous savez * où * le problème se pose. Mais à moins que vous ne publiez le code complet et exact, y compris la trace de la pile, vous devrez le faire vous-même. –

+0

Voici le code: private void readFiles() { Fichier [] files = inputDir.listFiles(); pour (int i = 0; i Mavin

23

Si vous n'avez pas besoin mutabilité pour votre carte finale, il y a Guava'sImmutableMap avec son Builder et putAll method qui, contrairement à Java's Map interface method, peut être enchaînée.

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 boucle pour putAllMaps d'arguments etc., mais je voulais montrer un concept.

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

  • ils sont hostiles nul (jeter NullPointerException - le cas échéant clé ou une valeur sur la carte est null)
  • Builder n » t accepte les clés en double (lève IllegalArgumentException si des clés en double ont été ajoutées).
74

Si vous savez que vous n'avez pas les clés en double, ou si vous voulez valeurs map2 pour les valeurs de écrasent 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 un BiFunction fourni par l'utilisateur pour fusionner les valeurs pour les clés en double. merge fonctionne sur des clés et des valeurs individuelles, vous devrez donc utiliser une boucle ou Map.forEach. Ici, nous concaténer 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 les clés en double et que vous voulez appliquer, vous pouvez utiliser une fonction de fusion qui jette un AssertionError:

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

Prise un retour en arrière de cette question spécifique, la bibliothèque de flux Java 8 fournit toMap et groupingByCollectors.Si vous fusionnez à plusieurs reprises des cartes dans une boucle, vous pouvez 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 concurrent.

8

Java 8 remplacement d'une doublure pour la fusion de deux cartes:

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

Le même en référence de la méthode:

defaultMap.forEach(destMap::putIfAbsent); 

Ou idemponent pour la solution de cartes d'origine 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 rapide immuable avec Guava qui fait s moins possible les opérations de copie intermédiaire:

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 Merge two maps with Java 8 pour les cas où les valeurs présentes dans les deux cartes doivent être combinées avec la fonction de cartographie.

20

One-liner en Java 8 API 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 est la capacité de passer d'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)) 
0

vous pouvez utiliser - addAll méthode

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 même clé - alors il remplacera la valeur de la clé de la première carte de hachage avec la valeur de la clé de la deuxième carte de hachage.

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

7

solution générique pour combiner deux cartes qui peuvent é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; 
}