3

How can we turn a List<Foo> towards a Map<propertyA, List<propertyB>> in the most optimal way by using java streams.

Beware: propertyA is NOT unique

//pseudo-code
class Foo
    propertyA //not unique
    List<propertyB>

So far I have the following:

fooList.stream()
       .collect(Collectors.groupingBy(Foo::propertyA, 
                    Collectors.mapping(Foo::propertyB, Collectors.toList())))

Resulting into a Map<propretyA, List<List<propretyB>>> which is not yet flattened for its value.

Ruslan
  • 6,090
  • 1
  • 21
  • 36
Terence
  • 10,533
  • 1
  • 14
  • 20

3 Answers3

5

You could use Java 9+ Collectors.flatMapping:

Map<propretyA, List<propretyB>> result = fooList.stream()
       .collect(Collectors.groupingBy(Foo::propertyA, 
                Collectors.flatMapping(foo -> foo.propertyB().stream(), 
                                       Collectors.toList())));

Another way (Java8+) is by using Collectors.toMap as in this answer.

And yet another Java8+ way is to just not use streams, but use Map.computeIfAbsent instead:

Map<propretyA, List<propretyB>> result = new LinkedHashMap<>();
fooList.forEach(foo -> result.computeIfAbsent(foo.propertyA(), k -> new ArrayList<>())
                             .addAll(foo.propertyB()));
fps
  • 33,623
  • 8
  • 55
  • 110
  • 3
    …and if Java 9 is not an option, a backport of `flatMapping` is at the end of [this answer](https://stackoverflow.com/a/39131049/2711488) – Holger Mar 13 '19 at 22:19
4

This should do it:

    fooList.stream()
           .collect(Collectors.toMap(Foo::getPropertyA, Foo::getPropertyBList, (l1, l2) -> {
               l1.addAll(l2);
               return l1;
           }));
Not a JD
  • 1,864
  • 6
  • 14
1

You could use Collectors.toMap with merge function where you have to decide which List<propertyB> has to be used when duplicating keys:

Map<propertyA, List<propertyB>> collect = list.stream()
        .collect(Collectors.toMap(Foo::getPropertyA, Foo::getList, (l1, l2) -> ???));

Otherwise you can get IllegalStateException. From javadoc:

If the mapped keys contains duplicates (according to Object.equals(Object)), an IllegalStateException is thrown when the collection operation is performed.

It is up to you how to implement merge function, but usually you just want to return the union of 2 lists like: l1.addAll(l2); return l1;

Ruslan
  • 6,090
  • 1
  • 21
  • 36