我正在尝试使用java流将下面嵌套的for循环转换为hashmap但是我在收集器步骤中受到了冲击。你能帮忙吗?
现有代码:
private static HashMap<String, Long> getOutput(List<Employee> eList) {
HashMap<String, Long> outputList = new HashMap<>();
for (Employee employee : eList) {
List<Department> departmentList = employee.getDepartmentList();
for (Department department : departmentList) {
if (department.getType().equals(DepartmentType.SCIENCE)) {
outputList.put(employee.getName(),department.getDepartmentId()));
}
}
}
return outputList;
}
到目前为止我尝试过:
private static HashMap<String, Long> getOutput(List<Employee> eList) {
return eList.stream()
.flatMap(emp -> emp.getDepartmentList().stream()
.filter(dept -> dept.getType().equals(DepartmentType.SCIENCE))
.collect(HashMap::new, ???)
}
答案 0 :(得分:3)
在您完成emp
之后,您的主要问题似乎是保留了流的当前flatMap
引用。要保留此引用,您需要flatMap
某种可以同时包含Employee和Department的类 - 例如泛型Tuple(也称为Pair)。
Java没有在其API中内置直观的Tuple类,因此您可以选择:
修改强>
评论(感谢@Holger!)已经明白,每个员工似乎有很多部门。我的原始代码冒着抛出异常的风险,因为会有重复的密钥,而OP的原始代码只会覆盖映射条目。考虑使用groupingBy
收集器并更改此方法的返回类型。
private static Map<String, List<Long>> getOutput(List<Employee> 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())))
}
原创(见上文编辑):
以下是使用选项3的一些更新代码:
private static Map<String, Long> getOutput(List<Employee> 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;
}
}
答案 1 :(得分:2)
这永远不会像流一样漂亮,因为您需要第一个过滤器中的部门来计算要放入地图的值。所以你需要两次过滤部门:第二次找到第一次给你积极匹配并获得其Id值的部门。
恕我直言,这段代码最好以其当前形式进行格式化,因为它可以更清楚地掌握它的确切含义,以及更简单的调试。相比之下,相同的代码转换为流:
return eList.stream()
.flatMap(emp -> emp.getDepartmentList().stream()
.filter(dep -> dep.getType().equals(DepartmentType.SCIENCE))).collect(
Collectors.toMap(Employee::getName, emp -> emp.getDepartmentList().stream()
.filter(dep ->dep.getType.equals(DepartmentType.SCIENCE)
.findFirst().get().getDepartmentId())), (s, a) -> a);
}
基本上,您的问题中缺少的是Collectors.toMap()
方法,该方法作为参数:
toMap()
方法,但如果插入了重复的值,它会抛出IllegalStateException
。答案 2 :(得分:0)
我知道这有点晚了,但这是对你已经很漂亮的答案的贡献+解释:
private static HashMap<String, Long> getOutput(List<Employee> eList) {
return eList
.stream() // for each Employee
.flatMap(emp -> emp.getDepartmentList() // get all his departments
.stream()
//filter departments by your predicate
.filter(dept -> dept.getType().equals(DepartmentType.SCIENCE))
// build an Entry with the employee and each department
.map(dept -> new SimpleEntry(emp.getName(),dept.getDepartmentId())))
// Each SimpleEntry<Name,DeptId> is then added to the your final Map
.collect(Collectors.toMap(SimpleEntry::getKey, SimpleEntry::getValue, (val1, val2) ->{ return val1;},HashMap::new));
}
SimpleEntry
只是Map.Entry
界面的实现:
public class SimpleEntry implements Entry<String, Long> {
private String name;
private Long deptId;
public SimpleEntry(String name, Long deptId) {
this.name = name;
this.deptId = deptId;
}
@Override
public String getKey() {
return this.name;
}
@Override
public Long getValue() {
return this.deptId;
}
@Override
public Long setValue(Long value) {
return this.deptId = value;
}
}