Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using Java 8 streams for aggregating list objects

Tags:

java-8

We are using 3 lists ListA,ListB,ListC to keep the marks for 10 students in 3 subjects (A,B,C).

Subject B and C are optional, so only few students out of 10 have marks in those subjects

Class Student{
String studentName;
int marks;
}

ListA has records for 10 students, ListB for 5 and ListC for 3 (which is also the size of the lists)

Want to know how we can sum up the marks of the students for their subjects using java 8 steam.

I tried the following

List<Integer> list = IntStream.range(0,listA.size() -1).mapToObj(i -> listA.get(i).getMarks() + 
listB.get(i).getMarks() + 
listC.get(i).getMarks()).collect(Collectors.toList());;

There are 2 issues with this

a) It will give IndexOutOfBoundsException as listB and listC don't have 10 elements

b) The returned list if of type Integer and I want it to be of type Student.

Any inputs will be very helpful

like image 265
cooldev Avatar asked Mar 10 '23 04:03

cooldev


1 Answers

You can make a stream of the 3 lists and then call flatMap to put all the lists' elements into a single stream. That stream will contain one element per student per mark, so you will have to aggregate the result by student name. Something along the lines of:

Map<String, Integer> studentMap = Stream.of(listA, listB, listC)
        .flatMap(Collection::stream)
        .collect(groupingBy(student -> student.name, summingInt(student -> student.mark)));

Alternatively, if your Student class has getters for its fields, you can change the last line to make it more readable:

Map<String, Integer> studentMap = Stream.of(listA, listB, listC)
        .flatMap(Collection::stream)
        .collect(groupingBy(Student::getName, summingInt(Student::getMark)));

Then check the result by printing out the studentMap:

studentMap.forEach((key, value) -> System.out.println(key + " - " + value));

If you want to create a list of Student objects instead, you can use the result of the first map and create a new stream from its entries (this particular example assumes your Student class has an all-args constructor so you can one-line it):

List<Student> studentList = Stream.of(listA, listB, listC)
        .flatMap(Collection::stream)
        .collect(groupingBy(Student::getName, summingInt(Student::getMark)))
        .entrySet().stream()
            .map(mapEntry -> new Student(mapEntry.getKey(), mapEntry.getValue()))
            .collect(toList());
like image 180
Thomas Kåsene Avatar answered Jun 03 '23 09:06

Thomas Kåsene