Рассмотрим следующий класс:
public class Order {
private String id;
private List<Order> orders = new ArrayList<>();
@Override
public String toString() {
return this.id;
}
// getters & setters
}
ПРИМЕЧАНИЕ. Важно отметить, что я не может изменить этот класс, потому что я потребляю его из внешнего API.
Также рассмотрим следующую иерархию заказов:
Order o1 = new Order();
o1.setId("1");
Order o11 = new Order();
o11.setId("1.1");
Order o111 = new Order();
o111.setId("1.1.1");
List<Order> o11Children = new ArrayList<>(Arrays.asList(o111));
o11.setOrders(o11Children);
Order o12 = new Order();
o12.setId("1.2");
List<Order> o1Children = new ArrayList<>(Arrays.asList(o11, o12));
o1.setOrders(o1Children);
Order o2 = new Order();
o2.setId("2");
Order o21 = new Order();
o21.setId("2.1");
Order o22 = new Order();
o22.setId("2.2");
Order o23 = new Order();
o23.setId("2.3");
List<Order> o2Children = new ArrayList<>(Arrays.asList(o21, o22, o23));
o2.setOrders(o2Children);
List<Order> orders = new ArrayList<>(Arrays.asList(o1, o2));
Что можно визуально представить таким образом:
1
1.1
1.1.1
1.2
2
2.1
2.2
2.3
Теперь я хочу сгладить эту иерархию заказов в List
, чтобы получить следующее:
[1, 1.1, 1.1.1, 1.2, 2, 2.1, 2.2, 2.3]
Мне удалось сделать это рекурсивно, используя flatMap()
(вместе со вспомогательным классом) следующим образом:
List<Order> flattened = orders.stream()
.flatMap(Helper::flatten)
.collect(Collectors.toList());
Это вспомогательный класс:
public final class Helper {
private Helper() {
}
public static Stream<Order> flatten(Order order) {
return Stream.concat(
Stream.of(order),
order.getOrders().stream().flatMap(Helper::flatten)); // recursion here
}
}
Следующая строка:
System.out.println(flattened);
Производит следующий вывод:
[1, 1.1, 1.1.1, 1.2, 2, 2.1, 2.2, 2.3]
Пока все хорошо. Результат абсолютно правильный.
Однако после прочтения этого вопроса у меня возникли некоторые проблемы в отношении использования flatMap()
в рекурсивном методе. В частности, я хотел знать, как расширяется поток (если этот термин). Поэтому я изменил класс Helper
и использовал peek(System.out::println)
, чтобы проверить это:
public static final class Helper {
private Helper() {
}
public static Stream<Order> flatten(Order order) {
return Stream.concat(
Stream.of(order),
order.getOrders().stream().flatMap(Helper::flatten))
.peek(System.out::println);
}
}
И результат был:
1
1.1
1.1
1.1.1
1.1.1
1.1.1
1.2
1.2
2
2.1
2.1
2.2
2.2
2.3
2.3
Я не уверен, что это вывод, который должен быть напечатан.
Итак, мне интересно, нормально ли позволить промежуточным потокам содержать повторяющиеся элементы. Кроме того, каковы плюсы и минусы такого подхода? Правильно ли, в конце концов, использовать flatMap()
таким образом? Есть ли лучший способ добиться того же?