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 Sets 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 of entrySet because we're not concerned with the map keys.
  • Apple::getColour is the keyMapper function used to extract the "thing" we wish to group by, in this case, the Apples colour.
  • v -> new HashSet<>(singleton(v)) is the valueMapper function used for the resulting map values
  • (l, r) -> {l.addAll(r); return l;} is the merge function used to combine two HashSet's when there is a key collision on the Apple'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 of entrySet 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 the toSet 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 of entrySet 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 function e -> new HashSet<>() and enters it into the map. we then add the Apple to the resulting set.
  • if the specified key a.getColour() is already associated with a value computeIfAbsent returns the existing value associated with it and then we call add(a) on the HashSet to enter the Apple 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