A non-recursive solution using concatenation of the streams followed by filtering out the "duplicated" entries which do not have lead field set:
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())
));
}
Test for the following setup:
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);
Output:
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])