Плоское дерево объектов при сохранении отца и ребенка

#java #recursion #tree #java-stream

Вопрос:

У меня есть сотрудник класса, который имеет древовидную структуру со свойством team

 public class Employee {

    private String name;
    private List<Employee> team;
}
 

Используя Java-лямбды, мне нужно преобразовать это дерево в список того же уровня при преобразовании класса Employee в следующий преобразованный сотрудник при сохранении имен в выделенных lead и subordinates свойствах. Таким образом, ConvertedEmployee в некотором смысле является узлом, который хранит родителей и детей. И мне нужен их список.

     public class ConvertedEmployee {

    private String name;
    private String lead;
    private List<String> subordinatesNames;
}
 

Таким образом, конечный результат должен быть List<ConvertedEmployee>

Я думал использовать рекурсию здесь.

Но я могу только сровнять дерево с землей и не могу восстановить его родителей.

РЕДАКТИРОВАТЬ: Ввод-это экземпляр объекта сотрудника, внутри которого в основном есть дерево team

Ответ №1:

Нерекурсивное решение, использующее объединение потоков с последующей фильтрацией «дублированных» записей, для которых не lead задано поле:

 public static List<ConvertedEmployee> convert(List<Employee> staff) {
    return staff
        .stream()
        .flatMap(e -> Stream.concat(
            // subordinates of current employee
            e.getTeam()
             .stream()
             .map(sub -> new ConvertedEmployee(
                 sub.getName(), e.getName(),
                 sub.getTeam().stream().map(Employee::getName).collect(Collectors.toList())
             )),
            // current employee by themself
            Stream.of(new ConvertedEmployee(
                 e.getName(), null,
                 e.getTeam().stream().map(Employee::getName).collect(Collectors.toList())
            ))
        ))
        .collect(Collectors.collectingAndThen(
            Collectors.toMap(
                ConvertedEmployee::getName,
                c -> c,
                (e1, e2) -> e1.getLead() != null ? e1 : e2.getLead() != null ? e2 : e1,
                LinkedHashMap::new
            ),
            map -> new ArrayList<>(map.values())
        ));
}
 

Проверьте следующую настройку:

 Employee intern = new Employee("intern", Collections.emptyList());
Employee junior = new Employee("junior", Collections.emptyList());
Employee middleDev = new Employee("mid", Arrays.asList(junior));
Employee devOps = new Employee("devOps", Collections.emptyList());
Employee teamLead = new Employee("teamLead", Arrays.asList(intern, middleDev));
Employee pm = new Employee("mgr", Arrays.asList(teamLead, devOps));

List<Employee> staff = Arrays.asList(intern, junior, middleDev, devOps, teamLead, pm);

convert(staff).forEach(System.out::println);
 

Выход:

 ConvertedEmployee(name=intern, lead=teamLead, subordinatesNames=[])
ConvertedEmployee(name=junior, lead=mid, subordinatesNames=[])
ConvertedEmployee(name=mid, lead=teamLead, subordinatesNames=[junior])
ConvertedEmployee(name=devOps, lead=mgr, subordinatesNames=[])
ConvertedEmployee(name=teamLead, lead=mgr, subordinatesNames=[intern, mid])
ConvertedEmployee(name=mgr, lead=null, subordinatesNames=[teamLead, devOps])
 

Комментарии:

1. Почти тот момент, когда я лично рекомендовал бы предпочесть подход с циклом for, а не принудительную подгонку решения с потоками.

2. логика очень похожа на то, что нужно, спасибо!, просто ввод-это не список сотрудников, а один корневой сотрудник, внутри которого есть дерево team