I am trying to convert the below nested for loop into hashmap using java stream but i got struck in the collector step. Could you please help?
Existing code:
private static HashMap getOutput(List eList) {
HashMap outputList = new HashMap<>();
for (Employee employee : eList) {
List departmentList = employee.getDepartmentList();
for (Department department : departmentList) {
if (department.getType().equals(DepartmentType.SCIENCE)) {
outputList.put(employee.getName(),department.getDepartmentId()));
}
}
}
return outputList;
}
So far i tried:
private static HashMap getOutput(List eList) {
return eList.stream()
.flatMap(emp -> emp.getDepartmentList().stream()
.filter(dept -> dept.getType().equals(DepartmentType.SCIENCE))
.collect(HashMap::new, ???)
}
解决方案
It seems like your main issue is preserving the stream's current emp reference after you've done the flatMap. To keep this reference, you will need to flatMap to some sort of class that can hold both the Employee and Department - such as a generic Tuple (aka Pair).
Java doesn't have an intuitive Tuple class built into it's API, so your options are:
Use a 3rd party library that provides a Tuple class (e.g. javatuples)
DIY: Build your own generic Tuple class (see related SO question)
Quick: Add a private inner class which is specifically intended for this lambda
Edit:
The comments (thanks @Holger!) have enlightened that it appears that there are many departments per employee. My original code risks throwing an exception since there would be duplicate keys, while the OP's original code simply overwrites the map entries. Consider using the groupingBycollector and changing the return type of this method.
private static Map> getOutput(List eList) {
return eList.stream()
// get a stream of employee / department pairs
.flatMap(emp -> emp.getDepartmentList().stream().map(dep -> new EmployeeDepartmentPair(emp, dep))
// filter the departments to SCIENCE
.filter(x -> x.department.getType().equals(DepartmentType.SCIENCE))
// group departmentIds by employee name
.collect(Collectors.groupingBy(x -> x.employee.getName(), Collectors.mapping(x -> x.department.getDepartmentId(), Collectors.toList())))
}
Original (see above edit):
Here's some updated code using option 3:
private static Map getOutput(List eList) {
return eList.stream()
.flatMap(emp -> emp.getDepartmentList().stream().map(dep -> new EmployeeDepartmentPair(emp, dep))
.filter(x -> x.department.getType().equals(DepartmentType.SCIENCE))
.collect(Collectors.toMap(x -> x.employee.getName(), x -> x.department.getDepartmentId()));
}
private static class EmployeeDepartmentPair {
public final Employee employee;
public final Department department;
public EmployeeDepartmentPair(Employee emp, Department d) {
this.employee = emp;
this.department = d;
}
}