Search code examples
javajava-streamjava-11

Java Streams - convert a list to a Map of Object with a List


I'm trying to convert a List into a Map of Objects where that Objects holds the List.

Consider the following classes:

class A {
  String code;  
}

class B {
  A a;
  String value;
}

AB {
  A a;
  List<B> bList;
}

I have a List<B> sourceList that I want to convert into a Map<String, AB> targetMap

If we have the following data for sourceList:

[
  {
     value = "foo",
     a = {code = "code1"}
  },
  {
     value = "bar"
     a = {code = "code1"}
  {
     value = "hello"
     a = {code = "code2"}
  }
]

The targetMap should look like this:

{
  "code1" =
  {
     a = {code = "code1"}
     bList =
     [
       {
         value = "foo",
         a = {code = "code1"}
       },
       {
         value = "bar",
         a = {code = "code1"}
       }
     ]
  },

  "code2" =
  {
    a = {code = "code2"}
    bList =
    [
      {
        value = "hello"
        a = {code = "code2"}
      }
    ]
  }
}

How can I do this?

I tried using the Collectors.groupingBy and Collectors.mapping but both looks like only results in a Map of a Collection.


Solution

  • Use groupingBy to group by getA().getCode() as usual. In the downstream, use Collectors.toList, similar to what the one-argument groupingBy collector does, but then, convert that List<B> into an AB using collectingAndThen.

    Map<String, AB> convert(List<B> list) {
        return list.stream().collect(Collectors.groupingBy(
                x -> x.getA().getCode(),
                Collectors.collectingAndThen(
                        Collectors.toList(),
                        // assuming AB has a constructor that takes an A and a List<B>
                        bs -> new AB(bs.get(0).getA(), bs)
                )
        ));
    }
    

    Since groupingBy does not create empty groups, bs is always non-empty, so it is safe to do bs.get(0).