Разделение карты в Java 8+
У меня есть Map<String, String>
и List<String>
. Я хотел бы разделить Map
на основе условий
foreach(map.key -> list.contains(map.key))
и создайте две Map
. Какой самый элегантный способ сделать это? Я на Java 11, так что вы можете бросить все, что вы хотите в ответах.
То, к чему я пришел сейчас, это:
map.entrySet()
.stream()
.collect(partitioningBy(e -> list.contains(o.getKey())));
но это дает Map<Boolean, List<Entry<String, String>>>
.
Ответы
Ответ 1
Вы можете уменьшить каждую группу, используя toMap
(в качестве нижестоящего коллектора):
Map<String, String> myMap = new HashMap<>();
myMap.put("d", "D");
myMap.put("c", "C");
myMap.put("b", "B");
myMap.put("A", "A");
List<String> myList = Arrays.asList("a", "b", "c");
Map<Boolean, Map<String, String>> result = myMap.entrySet()
.stream()
.collect(Collectors.partitioningBy(
entry -> myList.contains(entry.getKey()),
Collectors.toMap(Entry::getKey, Entry::getValue)
)
);
И для этого примера, который производит {false={A=A, d=D}, true={b=B, c=C}}
Ответ 2
Хотя partitioningBy
- это путь, когда вам нужны обе альтернативы в качестве выходных данных, основанных на условии. Еще один выход (полезный для создания карты на основе одного условия) - использовать Collectors.filtering
как:
Map<String, String> myMap = Map.of("d", "D","c", "C","b", "B","A", "A");
List<String> myList = List.of("a", "b", "c");
Predicate<String> condition = myList::contains;
Map<String, String> keysPresentInList = myMap.keySet()
.stream()
.collect(Collectors.filtering(condition,
Collectors.toMap(Function.identity(), myMap::get)));
Map<String, String> keysNotPresentInList = myMap.keySet()
.stream()
.collect(Collectors.filtering(Predicate.not(condition),
Collectors.toMap(Function.identity(), myMap::get)));
или, в качестве альтернативы, если вы могли бы обновить существующую карту на месте, то вы могли бы сохранить записи на основе их ключевого присутствия в списке, используя только одну строку:
myMap.keySet().retainAll(myList);
Ответ 3
Вы не можете создать две отдельные карты, используя потоки (по крайней мере, не самым элегантным способом). Не бойтесь использовать старый обычный forEach
, я думаю, что это довольно чистая версия:
Map<String, String> contains = new HashMap<>();
Map<String, String> containsNot = new HashMap<>();
for(Map.Entry<String, String> entry : yourMap.entrySet()) {
if (yourList.contains(entry.getKey())) {
contains.put(entry.getKey(), entry.getValue());
} else {
containsNot.put(entry.getKey(), entry.getValue());
}
}
Ответ 4
Вы можете отфильтровать map
, применив фильтрацию к исходной map
, например:
List<String> list = new ArrayList<>(); //List of values
Map<String, String> map = new HashMap<>();
Map<String, String> filteredMap = map.entrySet()
.stream()
.filter(e -> list.contains(e.getKey()))
.collect(Collectors.toMap(Entry::getKey, Entry::getValue));
Затем вы можете сравнить содержимое filteredMap
map
с исходной map
чтобы извлечь записи, которых нет в filteredMap
map
.
Ответ 5
Вы можете перебрать карту и использовать вкусности, представленные в Java 8+:
Map<Boolean, Map<String, String>> result = Map.of(true, new LinkedHashMap<>(),
false, new LinkedHashMap<>());
Set<String> set = new HashSet<>(list);
map.forEach((k, v) -> result.get(set.contains(k)).put(k, v));
Сначала мы создаем карту result
с двумя записями, по одной для каждого раздела. Значения LinkedHashMap
так что порядок вставки сохраняется.
Затем мы создаем HashSet
из списка, так что set.contains(k)
является операцией O(1)
(в противном случае, если бы мы сделали list.contains(k)
, это будет O(n)
для каждой записи карта, таким образом, получая общую временную сложность O(n^2)
, что плохо).
Наконец, мы перебираем входную карту и помещаем запись (k, v)
в соответствующий раздел согласно результату вызова set.contains(k)
.
Ответ 6
В качестве дополнения к ответу @ernest_k вы можете использовать функцию groupingBy
:
Map<String, String> myMap = new HashMap<>();
myMap.put("d", "D");
myMap.put("c", "C");
myMap.put("b", "B");
myMap.put("A", "A");
List<String> myList = Arrays.asList("a", "b", "c");
Function<Entry<String, String> , Boolean> myCondition = i -> myList.contains(i.getKey());
Map<Boolean,List<Entry<String, String>>> myPartedMap = myMap.entrySet()
.stream().collect(Collectors.groupingBy(myCondition));
System.out.println(myPartedMap);