Merge Map <String, List <String> Java 8 Stream
Я хотел бы объединить две карты с JAVA 8 Stream:
Map<String, List<String>> mapGlobal = new HashMap<String, List<String>>();
Map<String, List<String>> mapAdded = new HashMap<String, List<String>>();
Я пытаюсь использовать эту реализацию:
mapGlobal = Stream.of(mapGlobal, mapAdded)
.flatMap(m -> m.entrySet().stream())
.collect(Collectors.groupingBy(Map.Entry::getKey,
Collectors.mapping(Map.Entry::getValue,
Collectors.toList())
));
Однако эта реализация создает только результат:
Map<String, List<Object>>
Если один ключ не содержится в mapGlobal
, он будет добавлен как новый ключ с соответствующим списком String. Если ключ дублируется в mapGlobal
и mapAdded
, оба списка значений будут объединены как: A = {1, 3, 5, 7}
и B = {1, 2, 4, 6}
, затем A ∪ B = {1, 2, 3, 4, 5, 6, 7}
.
Ответы
Ответ 1
Вы можете сделать это, перебирая все записи в mapAdded
и объединяя их в mapGlobal
.
Далее выполняется перебор записей mapAdded
помощью вызова forEach(action)
где действие использует ключ и значение каждой записи. Для каждой записи мы вызываем merge(key, value, remappingFunction)
для mapGlobal
: это либо создаст запись под ключом k
и значением v
если ключ не существует, либо вызовет заданную функцию переназначения, если они уже существовали. Эта функция объединяет 2 списка, которые в этом случае сначала добавляются в TreeSet
для обеспечения уникальности и сортировки элементов и преобразования обратно в список:
mapAdded.forEach((k, v) -> mapGlobal.merge(k, v, (v1, v2) -> {
Set<String> set = new TreeSet<>(v1);
set.addAll(v2);
return new ArrayList<>(set);
}));
Если вы хотите запустить это потенциально параллельно, вы можете создать потоковый конвейер, получив в него entrySet()
и вызвав функции parallelStream()
. Но тогда вам нужно убедиться, что вы используете карту, поддерживающую параллелизм для mapGlobal
, например 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);
}));
Ответ 2
Использование foreach поверх Map может использоваться для объединения заданного массива.
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;
}
Ответ 3
Исходная реализация не создает результат типа Map<String, List<Object>>
, но Map<String, List<List<String>>>
. Вам понадобится дополнительный конвейер Stream для создания Map<String, List<String>>
.