Convert flat list elements into nested collection in java

Viewed 708

I have a list of elements and I need to group by certain elements of this list type and convert it to a nested collection. Here is what I mean-

Consider below -

class StudentA {
    private String name;
    private int age;
    private int subjectId;
    private String subjectName;
}

class StudentB {
    private String name;
    private int age;
    List<Subject> subject;
}

class Subject {
    private int subjectId;
    private String subjectName;
}

I have a List<StudentA> as my input. But in this list each student can have multiple subjects and hence student information is repeating (for eg: [new StudentA("A",22,1,"X"), new StudentA("A",22,2,"Y"), new StudentA("B",24,1,"X") ] and so on). This is not a good representation and I need data in below format-

List<StudentB>

Note that StudentB has list of Subject as an element.

Here is what i am thinking, but not convinced,

  • Plain old iterative way where I iterate through each element in original list and construct what I need
  • I also looked at java8 stream api which has groupby on collected data which looks promising, but i can't seem to convert from StudentA type in the list to my StudentB type

What would be a good and efficient way to perform this ? Thanks much !

1 Answers

If you group instances of StudentA by name, you'll get a Map<String,List<StudentA>>. Then you can stream the values of this map and convert them to instances of StudentB:

List<StudentB> bList =
    aList.stream() // Stream<StudentA>
         .collect(Collectors.groupingBy(StudentA::getName)) // Map<String,List<StudentA>>
         .values() // Collection<List<StudentA>>
         .stream() // Stream<List<StudentA>>
         .map(list -> {
             StudentB b = new StudentB();
             b.setName(list.get(0).getName());
             b.setAge(list.get(0).getAge());
             list.forEach(a -> b.addSubject(new Subject(a.getSubjectID(),a.getSubjectName())));
             return b;
         }) // Stream<StudentB>
         .collect(Collectors.toList());

I took the liberty of assuming the existence of some method names and constructors.

This solution has the downside of potentially creating multiple Subject instances for the same Subject. This can be avoided if you create a Map<Integer,Subject> that contains the Subjects indexed by their IDs, and only create new Subjects if the required subject ID is not already in that Map.

Related