如何使用java流将嵌套for循环转换为Hashmap

时间:2017-01-25 02:05:25

标签: java java-stream

我正在尝试使用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, ???)
              }

3 个答案:

答案 0 :(得分:3)

在您完成emp之后,您的主要问题似乎是保留了流的当前flatMap引用。要保留此引用,您需要flatMap某种可以同时包含Employee和Department的类 - 例如泛型Tuple(也称为Pair)。

Java没有在其API中内置直观的Tuple类,因此您可以选择:

  1. 使用提供元组类的第三方库(例如javatuples
  2. DIY:构建您自己的通用Tuple类(请参阅相关的SO question
  3. 快速:添加专门用于此lambda的私有内部类
  4. 修改

    评论(感谢@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()方法,该方法作为参数:

  1. 钥匙;
  2. 值(需要从密钥计算);
  3. 合并功能,告诉收集器在重复项目时该怎么做;在这种情况下,只保留第一个值。还有一个只有两个参数的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;
    }
}