【问题标题】:Make a map of Department to List of employee from employee object containing list of departments从包含部门列表的员工对象制作部门到员工列表的映射
【发布时间】:2021-09-25 13:20:31
【问题描述】:

问题:Employee 包含部门列表,我们现在有一个员工列表,如何从员工列表中获取 Map 之类的地图。

下面的代码可以解决问题,但我想知道如何有效地使用流 api 而不是 for 循环。

    Department a = new Department("a");
    Department b = new Department("b");
    Department c = new Department("c");

    Employee e1 = new Employee("e1", List.of(a, b));
    Employee e2 = new Employee("e2", List.of(c, b));
    Employee e3 = new Employee("e3", List.of(c, a));
    Employee e4 = new Employee("e4", List.of(a, b, c));

    List<Employee> employees = List.of(e1, e2, e3, e4);
    Set<Department> departments = employees.stream().flatMap(employee ->
            employee.getDepartments().stream()).collect(Collectors.toSet());

    for (Department d : departments) {
        for (Employee employee : employees) {
            if (employee.getDepartments().contains(d)) {
                if (!result.containsKey(d)) {
                    result.put(d, new ArrayList<Employee>());
                }
                result.get(d).add(employee);
            }
        }
    }
    return result;

【问题讨论】:

标签: java java-stream


【解决方案1】:

即使没有任何流魔术,也可能值得一提的是,您只需要一个循环对,而不是您已经拥有的(员工及其部门):

[...]
List<Employee> employees = List.of(e1, e2, e3, e4);
for (Employee employee : employees) {
    for (Department d : employee.getDepartments()) {
        if (!result.containsKey(d)) {
            result.put(d, new ArrayList<Employee>());
        }
        result.get(d).add(employee);
    }
}
return result;

然后你可以尝试一些 stream-flatmap-groupingby 魔术:

[...]
List<Employee> employees = List.of(e1, e2, e3, e4);
var result = employees.stream().flatMap(employee->employee.getDepartments().stream()
        .map(department->AbstractMap.SimpleImmutableEntry<>(department,employee))
    .collect(Collectors.groupingBy(pair->pair.getKey()));

这里的缺点是result 将成为Map&lt;Department,AbstractMap.SimpleImmutableEntry&lt;Department,Employee&gt;。 (AbstractMap.SimpleImmutableEntry 是一个 2 元素元组,只是它有一个不错的长名称)。

上面的 sn-p 里面可能有错字,实际上我只是使用 Map&lt;String,List&lt;String&gt;&gt; 作为员工部门的东西来运行你的任务,因为我不想编写补充类:

public static void main(String[] args) {
    var a="a";
    var b="b";
    var c="c";
    var empdep=new HashMap<String, List<String>>();
    empdep.put("e1", List.of(a, b));
    empdep.put("e2", List.of(c, b));
    empdep.put("e3", List.of(c, a));
    empdep.put("e4", List.of(a, b, c));
    System.out.println(empdep);
    
    var depemp=new HashMap<String, List<String>>();
    for(var employee:empdep.entrySet())
        for(var department:employee.getValue()) {
            if(!depemp.containsKey(department))
                depemp.put(department, new ArrayList<String>());
            depemp.get(department).add(employee.getKey());
        }
    System.out.println(depemp);
        
    System.out.println(
            empdep.entrySet().stream().flatMap(employee->employee.getValue().stream()
                    .map(department->new AbstractMap.SimpleImmutableEntry<>(department, employee.getKey())))
                    .collect(Collectors.groupingBy(pair->pair.getKey()))
            );
}

这段代码输出

{e1=[a, b], e2=[c, b], e3=[c, a], e4=[a, b, c]}
{a=[e1, e3, e4], b=[e1, e2, e4], c=[e2, e3, e4]}
{a=[a=e1, a=e3, a=e4], b=[b=e1, b=e2, b=e4], c=[c=e2, c=e3, c=e4]}

其中第一行是输入“列表”(这里只是一个映射,但循环其entrySet() 与列表相同),第二行是 for 循环对的结果,生成所需的地图,第三行是 stream-magic 的结果,但带有部门“内部”的部门-员工对列表。


那是昨天,今天就是今天。我对groupingBy()mapping() 有了更多了解。这个“线”

System.out.println(
    empdep.entrySet().stream().flatMap(employee->employee.getValue().stream()
        .map(department->new AbstractMap.SimpleImmutableEntry<>(department, employee.getKey())))
        .collect(Collectors.groupingBy(pair->pair.getKey(),Collectors.mapping(pair->pair.getValue(), Collectors.toList()))));

使用前面的 String-String 示例生成所需的输出,

{a=[e1, e3, e4], b=[e1, e2, e4], c=[e2, e3, e4]}

然后是完整的代码,带有EmployeeDepartment 类:

public class Test {
    public static void main(String[] args) {
        Department a = new Department("a");
        Department b = new Department("b");
        Department c = new Department("c");

        Employee e1 = new Employee("e1", List.of(a, b));
        Employee e2 = new Employee("e2", List.of(c, b));
        Employee e3 = new Employee("e3", List.of(c, a));
        Employee e4 = new Employee("e4", List.of(a, b, c));

        List<Employee> employees = List.of(e1, e2, e3, e4);
        Map<Department,List<Employee>> result=employees.stream()
            .flatMap(employee->employee.getDepartments().stream()
                .map(department->new Pair(department,employee)))
            .collect(Collectors.groupingBy(pair->pair.d,
                                           Collectors.mapping(pair->pair.e,
                                                              Collectors.toList())));
        System.out.println(result);
    }
    
    static class Department{final String name;Department(String name){this.name=name;}public String toString(){return name;}}
    static class Employee{final String name;final List<Department> departments;Employee(String name,List<Department> departments){this.name=name;this.departments=departments;}List<Department> getDepartments(){return departments;}public String toString() {return name;}}
    // this is just a helper class instead of AbstractMap.whatever
    static class Pair{final Department d;final Employee e;Pair(Department d,Employee e){this.d=d;this.e=e;}}
}

此代码生成所需的Map&lt;Department,List&lt;Employee&gt;&gt; result,并打印

{b=[e1, e2, e4], a=[e1, e3, e4], c=[e2, e3, e4]}

同样在IdeOne

【讨论】:

    【解决方案2】:

    你可以这样做:

    Map<Department, List<Employee>> map = 
        departments.stream()
                .collect(Collectors.toMap(
                            Function.identity(),
                            d -> employees.stream()
                                        .filter(e -> e.getDepartments().contains(d))
                                        .collect(Collectors.toList())
                        )
                );
    

    ONLINE DEMO

    【讨论】:

      【解决方案3】:

      抱歉,在移动设备上。暂时无法添加示例代码

      1. 流式传输所有员工
      2. 平面映射到 Pair (Apache) 或映射条目(JDK AbstractMap 简单不可变条目)
      3. 收集地图。键映射是部门,值映射。这是通过按部门分组,将员工存储为列表来完成的(请参阅相关问题:https://stackoverflow.com/a/49889657/1870799

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2013-05-23
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-08-02
        • 2012-10-19
        • 1970-01-01
        相关资源
        最近更新 更多