#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