Je voudrais fusionner deux Map avec Java 8 Stream:
Map<String, List<String>> mapGlobal = new HashMap<String, List<String>>();
Map<String, List<String>> mapAdded = new HashMap<String, List<String>>();
J'essaie d'utiliser cette implémentation:
mapGlobal = Stream.of(mapGlobal, mapAdded)
.flatMap(m -> m.entrySet().stream())
.collect(Collectors.groupingBy(Map.Entry::getKey,
Collectors.mapping(Map.Entry::getValue,
Collectors.toList())
));
Cependant, cette implémentation ne crée qu'un résultat comme:
Map<String, List<Object>>
Si une clé n'est pas contenue dans mapGlobal
, elle sera ajoutée en tant que nouvelle clé avec la liste de chaînes correspondante. Si la clé est dupliquée dans mapGlobal
et mapAdded
, les deux listes de valeurs seront fusionnées comme: A = {1, 3, 5, 7}
et B = {1, 2, 4, 6}
puis A ∪ B = {1, 2, 3, 4, 5, 6, 7}
.
Vous pouvez le faire en itérant sur toutes les entrées de mapAdded
et en les fusionnant dans mapGlobal
.
L'exemple suivant parcourt les entrées de mapAdded
en appelant forEach(action)
où l'action consomme la clé et la valeur de chaque entrée. Pour chaque entrée, nous appelons merge(key, value, remappingFunction)
sur mapGlobal
: cela créera soit l'entrée sous la clé k
et la valeur v
si la clé n'existait pas ou qu'elle invoquerait la fonction de remappage donnée si elle existait déjà. Cette fonction prend les 2 listes à fusionner, qui dans ce cas, sont d'abord ajoutées à un TreeSet
pour garantir à la fois les éléments uniques et triés et reconverties en liste:
mapAdded.forEach((k, v) -> mapGlobal.merge(k, v, (v1, v2) -> {
Set<String> set = new TreeSet<>(v1);
set.addAll(v2);
return new ArrayList<>(set);
}));
Si vous souhaitez exécuter cela potentiellement en parallèle, vous pouvez créer un pipeline Stream en obtenant la entrySet()
et en appelant parallelStream()
dessus. Mais alors, vous devez vous assurer d'utiliser une carte qui prend en charge la concurrence pour mapGlobal
, comme un ConcurrentHashMap
.
ConcurrentMap<String, List<String>> mapGlobal = new ConcurrentHashMap<>();
// ...
mapAdded.entrySet().parallelStream().forEach(e -> mapGlobal.merge(e.getKey(), e.getValue(), (v1, v2) -> {
Set<String> set = new TreeSet<>(v1);
set.addAll(v2);
return new ArrayList<>(set);
}));
L'utilisation de foreach sur Map peut être utilisée pour fusionner une liste d'arrays donnée.
public Map<String, ArrayList<String>> merge(Map<String, ArrayList<String>> map1, Map<String, ArrayList<String>> map2) {
Map<String, ArrayList<String>> map = new HashMap<>();
map.putAll(map1);
map2.forEach((key , value) -> {
//Get the value for key in map.
ArrayList<String> list = map.get(key);
if (list == null) {
map.put(key,value);
}
else {
//Merge two list together
ArrayList<String> mergedValue = new ArrayList<>(value);
mergedValue.addAll(list);
map.put(key , mergedValue);
}
});
return map;
}
L'implémentation d'origine ne crée pas de résultat comme Map<String, List<Object>>
, mais Map<String, List<List<String>>>
. Vous avez besoin d'un pipeline Stream supplémentaire pour produire Map<String, List<String>>
.
Utilisation de StreamEx
Map<String, List<String>> mergedMap =
EntryStream.of(mapGlobal)
.append(EntryStream.of(mapAdded))
.toMap((v1, v2) -> {
List<String> combined = new ArrayList<>();
combined.addAll(v1);
combined.addAll(v2);
return combined;
});
Si vous avez encore plus de cartes à fusionner, ajoutez-les simplement au flux
.append(EntryStream.of(mapAdded2))
.append(EntryStream.of(mapAdded3))