Convert Map<String, Object> to Map<String, Set<Object>> with filter and streams
Collectors.groupingBy
is more suitable than Collectors.toMap
for this task (though both can be used).
Map<String, List<Apple>> sortedApples =
appleMap.values()
.stream()
.collect(Collectors.groupingBy(Apple::getColour));
Or, to group them into Set
s use:
Map<String, Set<Apple>> sortedApples =
appleMap.values()
.stream()
.collect(Collectors.groupingBy(Apple::getColour,
Collectors.mapping(Function.identity(),
Collectors.toSet())));
or (as Aomine commented):
Map<String, Set<Apple>> sortedApples =
appleMap.values()
.stream()
.collect(Collectors.groupingBy(Apple::getColour, Collectors.toSet()));
if you want to proceed with toMap
you can get the result as follows:
map.values() // get the apples
.stream() // Stream<Apple>
.collect(toMap(Apple::getColour, // group by colour
v -> new HashSet<>(singleton(v)), // have values as set of apples
(l, r) -> {l.addAll(r); return l;})); // merge colliding apples by colour
- stream over the map
values
instead ofentrySet
because we're not concerned with the map keys. Apple::getColour
is thekeyMapper
function used to extract the "thing" we wish to group by, in this case, theApple
s colour.v -> new HashSet<>(singleton(v))
is thevalueMapper
function used for the resulting map values(l, r) -> {l.addAll(r); return l;}
is the merge function used to combine twoHashSet
's when there is a key collision on theApple
's colour.- finally, the resulting map is a
Map<String, Set<Apple>>
but this is better with groupingBy
and toSet
as downstream:
map.values().stream().collect(groupingBy(Apple::getColour, toSet()));
stream over the map
values
instead ofentrySet
because we're not concerned with the map keys.groups the
Apple
's by the provided classification function i.e.Apple::getColour
and then collect the values in a Set hence thetoSet
downstream collector.finally, the resulting map is a
Map<String, Set<Apple>>
short, readable and the idiomatic approach.
You could also do it without a stream:
Map<String, Set<Apple>> res = new HashMap<>();
map.values().forEach(a -> res.computeIfAbsent(a.getColour(), e -> new HashSet<>()).add(a));
- iterate over the map
values
instead ofentrySet
because we're not concerned with the map keys. - if the specified key
a.getColour()
is not already associated with a value, attempts to compute its value using the given mapping functione -> new HashSet<>()
and enters it into the map. we then add theApple
to the resulting set. - if the specified key
a.getColour()
is already associated with a valuecomputeIfAbsent
returns the existing value associated with it and then we calladd(a)
on theHashSet
to enter theApple
into the set. - finally, the resulting map is a
Map<String, Set<Apple>>
You can use Collectors.groupingBy
and Collectors.toSet()
Map<String, Set<Apple>> sortedApples = appleMap.values() // Collection<Apple>
.stream() // Stream<Apple>
.collect(Collectors.groupingBy(Apple::getColour, // groupBy colour
Collectors.mapping(a -> a, Collectors.toSet()))); // collect to Set