Java 8 многоуровневая группировка и сокращение - PullRequest
0 голосов
/ 02 февраля 2019

У меня есть сценарий, в котором у меня есть список объектов одного типа, и мне нужно создать из него список других объектов другого типа.

Ниже приведен код: у меня есть список сотрудникови нужно создать список EmployeeInfo из первого списка.Обратите внимание, что Employee имеет атрибут учетной записи, а EmployeeInfo имеет список учетных записей.В этом случае один и тот же сотрудник может иметь несколько учетных записей, поэтому в результирующем списке 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();
}

Пока вышеупомянутый кусокработает нормально, наконец, предоставив мне список employeeinfo, сгруппированный по dept, каждый со своим списком учетных записей, я хотел сделать это более функционально, например:

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

Вышеприведенная строка выдает ошибку: Несовместимые типы: T не конвертируется в Employee.Может кто-нибудь, пожалуйста, помогите мне разобраться, как решить эту проблему?

Спасибо!

Ответы [ 2 ]

0 голосов
/ 02 февраля 2019

есть ли причина, по которой вы сделали финал name в EmployeeInfo?если вы можете изменить, что это решение будет работать

, добавьте эти два метода в EmployeeInfo

public void setName(String name) {
    this.name = name;
}
public void AddAccount(String account) {
    this.accounts.add(account);
}

, а затем вы можете сделать это

Collector<Employee, EmployeeInfo, EmployeeInfo> empToInfo = Collector.of(
     () -> new EmployeeInfo("", new ArrayList<String>()),
            (info, e) -> { 
                info.AddAccount(e.getAccount());
                info.setName(e.getName());
                },
     (p1,p2) -> p1.addToList(p2));

Collector<Employee, ?, Collection<EmployeeInfo>> byName = collectingAndThen(groupingBy(Employee::getName, empToInfo), 
                  (Map<String, EmployeeInfo> finisher) -> {return finisher.values();});

Map<Integer, Collection<EmployeeInfo>> r2 = employees.stream().collect(groupingBy(Employee::getDept, byName));

, если хотите сохранитьEmployeeInfo неизменяемый, вы можете использовать сокращение вместо коллекции, и это будет выглядеть так:

Map<Integer, Collection<EmployeeInfo>> result2 = employees.stream().collect(groupingBy(Employee::getDept,
             collectingAndThen(groupingBy(Employee::getName, reducing(new EmployeeInfo("", new ArrayList<String>()), 
                                                                      empl3 -> new EmployeeInfo(empl3.getName(),Arrays.asList(empl3.getAccount())), 
                                                                      (inf1, inf2) -> inf1.addToList(inf2))), 
                                finisher -> finisher.values()))); 
0 голосов
/ 02 февраля 2019

Это кажется немного сложным, но нет ничего невозможного

Сначала выполните группировку в dept и введите ключ карты в качестве dept и значения в виде List

second вам нужносделать внутренний groupBy сотрудником name

Затем получить значения из внутренней карты и преобразовать в информацию сотрудника со списком счетов

  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=[]]]}
...