I am learning Java 8 streams and I am trying to refactor a method.
Say I have a School class and a school map
that stores all school objects by Id. Each school object contains a student map
that stores a bunch of students.
In this scenario, the student Ids are unique across the schools.
I have a function that retrieves a student by the id across all schools.
public Student getStudent(Map<String, School> schoolMap, String studentId) {
return schoolMap.values().stream()
.map(School::getStudentIdToStudentMap)
.filter(map -> map.containsKey(studentId))
.map(map -> map.get(studentId))
.findAny().get();
}
Now, I want to change the function to use schoolId
as a filter if available.
public Student getStudent(Map<String, School> schoolMap,
String schoolId /* can be null */,
String studentId)
{
// TODO: Something that I have tried
return schoolMap.get(schoolId)
.getStudentIdToStudentMap()
.get(studentId);
}
Is there a good way I can combine these two functions? If schoolId
is null, get the student from all schools. Else just lookup in the particular school and retrieve the student?
I bet this is what are you looking for:
public Student getStudent(Map<String, School> schoolMap, String schoolId, String studentId)
{
return Optional.ofNullable(schoolId) // schoolId might be null
.map(id -> Stream.of(schoolMap.get(id))) // get Stream<School> by else
.orElse(schoolMap.values().stream()) // ... get Stream of all Schools
.flatMap(i -> i.getStudentIdToStudentMap() // students from 1/all schools ...
.entrySet().stream()) // flat map to Stream<Entry<..,..>>
.collect(Collectors.toMap( // collect all entries bu key/value
Entry::getKey, Entry::getValue)) // ... Map<String,Student>
.getOrDefault(studentId, null); // get Student by id or else null
}
You have to search in either the only known school or in all the schools. The idea is based on the common characteristics of the searching process. The finding in any school remains the same regardless you iterate one known school or all the ones.
Alternatively get List<Student>
from Optional
,
public Student getStudent(Map<String, School> schoolMap, String schoolId, String studentId)
{
return Optional.ofNullable(schoolId) // schoolId might be null
.map(i -> Collections.singletonList(schoolMap.get(i))) // add School into List
.orElse(new ArrayList<>(schoolMap.values())) // ... else all schools
.stream()
.map(i -> i.getStudentIdToStudentMap() // get Map of students from 1/all
.get(studentId)) // ... find by studentId
.filter(Objects::nonNull) // get rid of nulls
.findFirst().orElse(null); // get Student by id or else null
}
This would be the clearest way IMO:
public Student getStudent(Map<String, School> schoolMap,
String schoolId /* can be null */,
String studentId){
if(schoolId == null){
return getStudent(schoolMap, studentId); // delegate to overload
} else{
return schoolMap.get(schoolId)
.getStudentIdToStudentMap()
.get(studentId);
}
}
It is not always needed to do everything with streams. So split the two apart. Either search through all maps with streams, or just select the student from the schoold where you have the id from.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With