Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Partitioning a Map in Java 8+

I have a Map<String, String> and a List<String>. I'd like to partition the Map based on the condition

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

and produce two Map(s). What's the most elegant way to do so? I'm on Java 11, so you can throw everything you want in the answers.

What I came up to for now is:

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

but that gives a Map<Boolean, List<Entry<String, String>>>.

like image 722
LppEdd Avatar asked Feb 06 '19 09:02

LppEdd


People also ask

How to partition Map in java?

First, we create a result map with two entries, one for each partition. The values are LinkedHashMap s so that insertion order is preserved. Then, we create a HashSet from the list, so that invoking set. contains(k) is a O(1) operation (otherwise, if we did list.

What is partitioningBy in java 8?

Collectors partitioningBy() method in Java 8 The partioningBy() method returns a Collector that partitions the input elements according to a Predicate, and organizes them into a Map<Boolean, List<T>>.

What does partition mean java?

java. In number theory, * a partition of N is a way to write it as a sum of positive integers. * Two sums that differ only in the order of their terms are considered * the same partition.


2 Answers

You can reduce each group using toMap (as a downstream collector):

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)                     )         ); 

And for this example, that produces {false={A=A, d=D}, true={b=B, c=C}}

like image 189
ernest_k Avatar answered Oct 20 '22 00:10

ernest_k


Though partitioningBy is the way to go when you need both the alternatives as an output based on the condition. Yet, another way out (useful for creating map based on a single condition) is to use Collectors.filtering as :

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)));

or alternatively, if you could update the existing map in-place, then you could retain entries based on their key's presence in the list using just a one-liner:

myMap.keySet().retainAll(myList);
like image 39
Naman Avatar answered Oct 20 '22 00:10

Naman