Разделение карты в Java 8+ - PullRequest
       1

Разделение карты в Java 8+

0 голосов
/ 06 февраля 2019

У меня есть Map<String, String> и List<String>.Я хотел бы разделить Map на основе условия

foreach(map.key -> list.contains(map.key))

и произвести два Map (s).Какой самый элегантный способ сделать это?Я на Java 11, так что вы можете добавить в ответы все, что вы хотите.

На данный момент я пришел к следующему:

map.entrySet()
   .stream()
   .collect(partitioningBy(e -> list.contains(o.getKey())));

, но это дает Map<Boolean, List<Entry<String, String>>>.

Ответы [ 6 ]

0 голосов
/ 06 февраля 2019

Вы можете выполнить итерацию карты и использовать вкусности, представленные в 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 s, так что порядок вставки сохраняется.

Затем мы создаем HashSet из списка, так что вызов set.contains(k) является операцией O(1) (в противном случае, если бы мы сделали list.contains(k), это было бы O(n) для каждой записикарта, что дает общую сложность времени O(n^2), что плохо).

Наконец, мы повторяем входную карту и помещаем запись (k, v) в соответствующий раздел согласно результату вызоваset.contains(k).

0 голосов
/ 06 февраля 2019

Хотя 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);
0 голосов
/ 06 февраля 2019

В качестве дополнения к ответу @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);
0 голосов
/ 06 февраля 2019

Вы можете отфильтровать 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, чтобы извлечь записи, которыеотсутствуют в filteredMap.

0 голосов
/ 06 февраля 2019

Вы можете уменьшить каждую группу, используя 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}}

0 голосов
/ 06 февраля 2019

Вы не можете создать две отдельные карты, используя потоки (по крайней мере, не самым элегантным способом).Не бойтесь использовать старый обычный 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());
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...