Да приемем, че имаме колекция от обекти, които се идентифицират с уникални String
s, заедно с клас Tree
, който дефинира йерархия върху тях. Този клас е имплементиран с помощта на Map
от възли (представени от техните идентификатори) до Collection
s от техните съответни идентификатори на деца.
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
s,Spliterator
s, ...), образуват стек и следователно изискването за памет във всеки даден момент е в същия ред на сложност като дълбочината на дървото.Доколкото разбирам това, веднага щом извърша операция за прекратяване на
Stream
, върната отdescendants
, извикването на основно ниво къмflatMap
ще накара всички съдържащи сеStream
s – по едно за всяко (рекурсивно) повикване къмdescendants
– да бъдат реализирани незабавно. По този начин получениятStream
е мързелив само на първото ниво на рекурсия, но не и след това. (Редактирано според отговора на Тагир Валеев.)
Ако съм разбрал правилно тези точки, въпросът ми е следният: Как мога да дефинирам descendants
, така че полученото Stream
да е мързеливо?
Бих искал решението да бъде възможно най-елегантно, в смисъл, че предпочитам решение, което оставя състоянието на итерация имплицитно. (За да изясня какво имам предвид с това: знам, че мога да напиша Spliterator
, което върви по дървото, като същевременно поддържа изричен стек от Spliterator
s на всяко ниво. Бих искал да избегна това.)
(Възможно ли е начин в Java да се формулира това като работен поток производител-потребител, както може да се използва в езици като Julia и Go?)
Spliterator
изглежда е това, което искате... - person fge   schedule 23.09.2015TreeTraverser
на Guava и след това да го обвия катоStream
. - person Louis Wasserman   schedule 24.09.2015