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
?
map3 = new HashMap<>();
map3.putAll(map1);
map3.putAll(map2);
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 groupingBy
Collectors . 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é.
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))
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.
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 putAll
Maps
à 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?):
NullPointerException
- si une clé ou une valeur de map est nulle)IllegalArgumentException
si des clés en double ont été ajoutées).HashMap a une méthode putAll
.
http://download.Oracle.com/javase/6/docs/api/Java/util/HashMap.html
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 <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;
}
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]
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]}
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;
}
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);
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
}
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]
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);