Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java 8 Stream Collect Sets

I have two maps, namely

Map<String, Set<String>> courseTeacherMap = {course1: [teacher1, teacher2], ...}
Map<String, Set<String>> teacherStudentMap = {teacher1: [student1, student2], ...}

And I defined a class courseStudentPair that has a very simple structure

public class courseStudentPair{
    String studentName; // Taken from teacherStudentMap
    String courseName; // Taken from courseTeacherMap
}

And my goal is to get a Set<courseStudentPair> out of the two maps. As long as a teacher A is teaching a course C, every student that is in the value set of key A in teacherStudentMap is considered to be a student taking C.

For example, given

Map<String, Set<String>> courseTeacherMap = {c1: [t1], c2:[t2], c3:[t1, t2]}
Map<String, Set<String>> teacherStudentMap = {t1: [s1], t2:[s1, s2]}

The result should be *(student, course) denotes a courseStudentPair object in the example below*

Set<courseStudentPair> result = [(c1, s1), (c2, s1), (c2, s2), (c3, s1), (c3, s2)]

It's quite straightforward to do it with for loops, but I am learning the stream function in Java 8 and this seems quite complicated to me. You can assume the courseStudentPair class has constructor or builder defined.

like image 681
Zoff Avatar asked Oct 17 '22 05:10

Zoff


2 Answers

In the same spirit, you can generate each combination of (course, teacher) and then lookup for the students associated with this teacher. This may generate duplicates (for instance (c3, s1)), so be sure your CourseStudentPair class implements equals() and hashCode() based on those two fields.

import static java.util.Collections.emptySet;
import static java.util.stream.Collectors.toSet;

...

Set<CourseStudentPair> result =
    courseTeacherMap.entrySet()
                    .stream()
                    .flatMap(e -> e.getValue()
                                   .stream()
                                   .flatMap(t -> teacherStudentMap.getOrDefault(t, emptySet()).stream().map(s -> new CourseStudentPair(e.getKey(), s))))
                    .collect(toSet());
/*
 Output:

 CourseStudentPair{studentName='c1', courseName='s1'}
 CourseStudentPair{studentName='c2', courseName='s2'}
 CourseStudentPair{studentName='c2', courseName='s1'}
 CourseStudentPair{studentName='c3', courseName='s2'}
 CourseStudentPair{studentName='c3', courseName='s1'}
*/
like image 69
Alexis C. Avatar answered Oct 21 '22 09:10

Alexis C.


List<Pair<String, String>> result = courseTeacherMap.entrySet()
            .stream()
            .flatMap(entry -> Optional.ofNullable(entry.getValue())
                    .orElse(new HashSet<>())
                    .stream()
                    .flatMap(teacher -> Optional.ofNullable(teacherStudentMap.get(teacher))
                            .orElse(new HashSet<>())
                            .stream()
                            .map(student -> Pair.of(entry.getKey(), student))))
            .distinct()
            .collect(Collectors.toList());

I edited to make it null-safe, in case a teacher has no students for example or your Map might have a key mapped to null for example.

like image 23
Eugene Avatar answered Oct 21 '22 09:10

Eugene