How to create a map<k,v> between first and last nested object using Java streams?

Viewed 76

Assuming I have the following classes:

class A {
    int id;
    List<B> b;
}

class B {
    int id;
}

I want to create a map between A.id to the list of B.id (Map<Integer, List<Integer>> , where key = A.id, and List<Integer> corresponds to the list of B.id fields for each A ). I tried various combinations of Collectors.groupingBy and Collectors.mapping, but to no effect. Can somebody help me out ?

4 Answers

You may use the toMap collector with a merge function to solve this problem. Here's how it looks.

Map<Integer, List<Integer>> resultMap = aList.stream()
    .collect(Collectors.toMap(A::getId,
        a -> a.getB().stream().map(B::getId)
            .collect(Collectors.toList()), (l1, l2) -> {
                    l1.addAll(l2);
                    return l1;
                }));

However, if a given set of A objects have distinct id values, then you can merely dispense with the merge function, which is the third argument to the toMap collector. Here's the simplified version.

Map<Integer, List<Integer>> resultMap = aList.stream()
    .collect(Collectors.toMap(A::getId, 
        a -> a.getB().stream().map(B::getId)
            .collect(Collectors.toList())));
List<A> aList;
//initialize aList here...
Map<String, List<Integer>> aMap = aList.stream().collect(
        Collectors.toMap(element -> element.id,
                         element -> element.b
                                 .stream()
                                 .map(b -> b.id)
                                 .collect(Collectors.toList())
        )
);

You can do it in java-8 using Collectors.mapping to only map List<B> b while groping and then Collectors.collectingAndThen for the final transformation of value List<List<B>> to List<Integer>

Map<Integer, List<Integer>> result = list.stream()
            .collect(Collectors.groupingBy(A::getId,
                    Collectors.mapping(A::getB, Collectors.collectingAndThen(Collectors.toList(),
                            l -> l.stream().flatMap(List::stream).map(B::getId).collect(Collectors.toList())))));

Java 9 added flatMapping collector, which is perfect for this:

// using static imports of various Collectors
listOfA.stream().collect(
  groupingBy(
    A::getId,
    flatMapping(
      a -> a.getB().stream().map(B::getId),
      toList()
    )
  )
);
Related