首页 > 解决方案 > Java 8 多级分组和归约

问题描述

我有一个场景,我有一个类型的对象列表,我需要从中创建另一个类型的另一个对象的列表。

下面是代码:我有一个员工列表,需要从第一个列表中创建 EmployeeInfo 列表。请注意,Employee 有一个帐户属性,而 EmployeeInfo 有一个帐户列表。在这种情况下,同一个员工可以有多个账户,因此在生成的员工信息列表中,每个信息对象都会有一个账户列表。这是我的做法:

public class Employee {

private final int dept;

private final String name;

private final String city;

private final String account;

public Employee(int dept, String name, String city, String account) {
    this.dept = dept;
    this.name = name;
    this.city = city;
    this.account = account;
}

public int getDept() {
    return dept;
}

public String getName() {
    return name;
}

public String getCity() {
    return city;
}

public String getAccount() {
    return account;
}

}

public class EmployeeInfo {

private final String name;

private final List<String> accounts;

public EmployeeInfo(String name, List<String> accounts) {
    this.name = name;
    this.accounts = accounts;
}

public String getName() {
    return name;
}

public List<String> getAccounts() {
    return accounts;
}

public EmployeeInfo addToList(EmployeeInfo employeeInfo) {
    List<String> l = new ArrayList<>();
    l.addAll(this.getAccounts());
    l.addAll(employeeInfo.getAccounts());
    return new EmployeeInfo(employeeInfo.name, l);
}

}

测试类:

public static void main(String[] args){
    List<Employee> employees = new ArrayList<>();
    //tradeId, secPool, datasetid, restricValue
    employees.add(new Employee(1, "Mary", "Boston", "A1"));
    employees.add(new Employee(1, "Mary", "Boston", "A2"));
    employees.add(new Employee(1, "Alex", "NYC", ""));
    employees.add(new Employee(2, "Peter", "DC", ""));
    employees.add(new Employee(1, "Sophia", "DC", "A4"));

    TestEmployeeGrouping testEmployeeGrouping = new TestEmployeeGrouping();

    Map<Integer, List<EmployeeInfo>> result = new HashMap<>();
    Map<Integer, Map<String, List<Employee>>> map =  employees.stream().collect(groupingBy(Employee::getDept, groupingBy(testEmployeeGrouping::createKey)));
    map.forEach((integer, stringListMap) -> {
        List<EmployeeInfo> employeeInfos = createInfo(stringListMap);
        result.put(integer, employeeInfos);
    });


}

private static List<EmployeeInfo> createInfo(Map<String,List<Employee>> stringListMap) {
    List<EmployeeInfo> employeeInfos = new ArrayList<>();
    stringListMap.forEach((s, employees) -> {
        List<String> accounts = employees.stream().map(Employee::getAccount).collect(Collectors.toList());
        employeeInfos.add(new EmployeeInfo(employees.get(0).getName(), accounts));
    });
    return employeeInfos;
}


private String createKey(Employee employee) {
    return employee.getDept() + employee.getName();
}

虽然上面的部分工作正常,最后给了我一个按部门分组的员工信息列表,每个都有它的帐户列表,我想以一种更实用的方式来做,比如:

employees.stream().collect(groupingBy(Employee::getDept, groupingBy(testEmployeeGrouping::createKey, reducing(EmployeeInfo::addToList))));

上面的行抛出错误:不兼容的类型:T 不能转换为 Employee。有人可以帮我弄清楚如何解决这个问题吗?

谢谢!

标签: javajava-8java-stream

解决方案


这看起来有点复杂,但没有什么是不可能的

首先在 dept 上进行分组,并将 map 键命名为 dept 并将值命名为 List

其次,您需要按员工进行内部 groupByname

然后从内部映射中获取值并使用帐户列表转换为员工信息

  Map<Integer, List<EmployeeInfo>> empInfo = employees.stream().collect(Collectors.groupingBy(Employee::getDept)).entrySet()
                         .stream().collect(Collectors.toMap(Map.Entry::getKey, value->value.getValue().stream().collect(Collectors.groupingBy(Employee::getName)).values()
                                 .stream().map(emp->new EmployeeInfo(emp.stream().findFirst().get().getName(), emp.stream().map(Employee::getAccount).collect(Collectors.toList())))
                                 .collect(Collectors.toList())));

输出

{1=[EmployeeInfo [name=Alex, accounts=[]], EmployeeInfo [name=Sophia, accounts=[A4]], EmployeeInfo [name=Mary, accounts=[A1, A2]]], 
 2=[EmployeeInfo [name=Peter, accounts=[]]]}

推荐阅读