14

I want to use a Java Stream to run over a List of POJOs, such as the list List<A> below, and transform it into a Map Map<String, Set<String>>.

For example, class A is:

class A {
    public String name;
    public String property;
}

I wrote the code below that collects the values into a map Map<String, String>:

final List<A> as = new ArrayList<>();
// the list as is populated ...

// works if there are no duplicates for name
final Map<String, String> m = as.stream().collect(Collectors.toMap(x -> x.name, x -> x.property));

However, because there might be multiple POJOs with the same name, I want the value of the map be a Set. All property Strings for the same key name should go into the same set.

How can this be done?

// how do i create a stream such that all properties of the same name get into a set under the key name
final Map<String, Set<String>> m = ???
Sartorius
  • 499
  • 5
  • 12
tkja
  • 1,950
  • 5
  • 22
  • 40

4 Answers4

29

groupingBy does exactly what you want:

import static java.util.stream.Collectors.*;
...
as.stream().collect(groupingBy((x) -> x.name, mapping((x) -> x.property, toSet())));
Nevay
  • 784
  • 5
  • 9
7

@Nevay 's answer is definitely the right way to go by using groupingBy, but it is also achievable by toMap by adding a mergeFunction as the third parameter:

as.stream().collect(Collectors.toMap(x -> x.name, 
    x -> new HashSet<>(Arrays.asList(x.property)), 
    (x,y)->{x.addAll(y);return x;} ));

This code maps the array to a Map with a key as x.name and a value as HashSet with one value as x.property. When there is duplicate key/value, the third parameter merger function is then called to merge the two HashSet.

PS. If you use Apache Common library, you can also use their SetUtils::union as the merger

SwiftMango
  • 15,092
  • 13
  • 71
  • 136
3

Same Same But Different

Map<String, Set<String>> m = new HashMap<>();

as.forEach(a -> {
    m.computeIfAbsent(a.name, v -> new HashSet<>())
            .add(a.property);
    });
Alexey Filippov
  • 236
  • 2
  • 5
  • welcome to the Stackoverflow, i'd like to suggest you to be more descriptive if you want your answer be considered valuable and helpful and get a good feedback – MohammadReza Alagheband Sep 17 '18 at 07:23
0

Also, you can use the merger function option of the Collectors.toMap function Collectors.toMap(keyMapper,valueMapper,mergeFunction) as follows:

final Map<String, String> m = as.stream()
                                .collect(Collectors.toMap(
                                          x -> x.name, 
                                          x -> x.property,
                                          (property1, property2) -> property1+";"+property2);
KayV
  • 12,987
  • 11
  • 98
  • 148