Предположим, у нас есть набор объектов, идентифицируемых уникальными String
, а также класс Tree
, определяющий для них иерархию. Этот класс реализован с использованием Map
от узлов (представленных их идентификаторами) до Collection
идентификаторов соответствующих дочерних узлов.
class Tree {
private Map<String, Collection<String>> edges;
// ...
public Stream<String> descendants(String node) {
// To be defined.
}
}
Я хочу включить потоковую передачу потомков узла. Вот простое решение:
private Stream<String> children(String node) {
return edges.getOrDefault(node, Collections.emptyList()).stream();
}
public Stream<String> descendants(String node) {
return Stream.concat(
Stream.of(node),
children(node).flatMap(this::descendants)
);
}
Прежде чем продолжить, я хотел бы сделать следующие утверждения об этом решении. (правильно ли я говорю об этом?)
Обход
Stream
, возвращенного изdescendants
, потребляет ресурсы (время и память) — относительно размера дерева — в том же порядке сложности, что и ручное кодирование рекурсии. В частности, промежуточные объекты, представляющие состояние итерации (Stream
с,Spliterator
с, ...), образуют стек, и поэтому потребность в памяти в любой момент времени находится в том же порядке сложности, что и глубина дерева.Насколько я понимаю это, как только я выполняю завершающую операцию над
Stream
, возвращенным изdescendants
, корневой вызовflatMap
вызовет немедленную реализацию всех содержащихсяStream
s — по одному для каждого (рекурсивного) вызоваdescendants
. Таким образом, результирующийStream
ленив только на первом уровне рекурсии, но не выше. (Отредактировано в соответствии с ответом Тагира Валеева.)
Если я правильно понял эти пункты, мой вопрос заключается в следующем: Как я могу определить descendants
так, чтобы результирующее Stream
было ленивым?
Я хотел бы, чтобы решение было максимально элегантным, в том смысле, что я предпочитаю решение, которое оставляет состояние итерации неявным. (Чтобы пояснить, что я имею в виду: я знаю, что могу написать Spliterator
, который проходит по дереву, поддерживая явный стек из Spliterator
на каждом уровне. Я бы хотел этого избежать.)
(Возможно ли в Java сформулировать это как рабочий процесс производитель-потребитель, как это можно использовать в таких языках, как Julia и Go?)
Spliterator
кажется тем, что вам нужно... - person fge   schedule 23.09.2015TreeTraverser
в Guava, а затем обернул бы его какStream
. - person Louis Wasserman   schedule 24.09.2015