Если я ясно понимаю:
- вам нужен поток всех узлов в вашем дереве. Это правильно?
- У вас есть функция
process
, возвращающая прямых потомков узла
Если это так, и вы действительно хотите исключить любую рекурсию, Spliterator API - это путь к go. Вы создаете потоковый итератор для поглощения элементов в потоке. Любой стандартный Java Stream использует Spliterator под капотом.
EDIT : См. Реализацию, приведенную в конце примера кода (просмотр в глубину).
Однако Если вы разрешите рекурсию, то будет легко создать поток в глубину. В простом примере ниже показаны оба подхода:
import java.util.Arrays;
import java.util.Spliterator;
import java.util.Stack;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
public class Main {
/**
* Sample in-memory node class
*/
static class Node {
Node[] children;
final String name;
Node(String name) {
this.name = name;
}
@Override
public String toString() {
return name;
}
}
/**
* Simple implementation of your process function, giving back direct children.
* @param node Node to get children for.
* @return A stream of available children, can be empty.
*/
static Stream<Node> process(Node node) {
return node.children == null? Stream.empty() : Arrays.stream(node.children);
}
/**
* Recursion entrypoint. Allow to perform depth-first browsing.
*/
static Stream<Node> recurse(Node input) {
if (input.children == null || input.children.length < 1) return Stream.of(input);
else return Stream.concat(
Stream.of(input),
Arrays.stream(input.children)
.flatMap(Main::recurse)
);
}
public static void main(String[] args) throws InterruptedException {
/* Simple tree :
* ROOT
* - A
* -- AA
* -- AB
* --- ABA
* -- AC
* - B
* -- BA
* -- BB
* - C
*/
Node root = new Node("ROOT");
Node a = new Node("A");
Node b = new Node("B");
Node c = new Node("C");
root.children = new Node[]{a, b, c};
Node aa = new Node("AA");
Node ab = new Node("AB");
Node ac = new Node("AC");
a.children = new Node[]{aa, ab, ac};
Node ba = new Node("BA");
Node bb = new Node("BB");
b.children = new Node[]{ba, bb};
Node aba = new Node("ABA");
ab.children = new Node[]{aba};
System.out.println("RECURSIVE BROWSING");
Stream.of(root).flatMap(Main::recurse)
.forEach(System.out::println);
System.out.println("NON-RECURSIVE USING SINGLE-THREAD SPLITERATOR");
final NodeSpliterator spliterator = new NodeSpliterator(root);
StreamSupport.stream(spliterator, false)
.forEach(System.out::println);
}
static class NodeSpliterator implements Spliterator<Node> {
private final Stack<Node> nodeQueue = new Stack<>();
NodeSpliterator(Node root) {
nodeQueue.push(root);
}
@Override
public boolean tryAdvance(Consumer<? super Node> action) {
if (nodeQueue.empty()) return false; // No more nodes, end of this fork of the stream.
final Node next = nodeQueue.pop();
// Iterate on next available node
action.accept(next);
// Sink direct children for next iteration
final int endOfQueue = nodeQueue.size();
process(next).forEach(node -> nodeQueue.add(endOfQueue, node));
// Notify Stream API that an element has successfully been processed.
return true;
}
@Override
public Spliterator<Node> trySplit() {
// Little trick : Parallelism of ordered streams requires the fork to distribute a prefix of
// Available elements. I'll keep things simple here, I let you work on that.
return null;
}
@Override
public long estimateSize() {
return Long.MAX_VALUE; // Would require external metadata for estimation, or womplete browsing for exact count
}
@Override
public int characteristics() {
// Note : you can add Spliterator.CONCURRENT below once you've resolved how to implement trySplit.
return ORDERED|NONNULL;
}
}
}
Этот код выдает следующий вывод:
RECURSIVE BROWSING
ROOT
A
AA
AB
ABA
AC
B
BA
BB
C
NON-RECURSIVE USING SINGLE-THREAD SPLITERATOR
ROOT
A
AA
AB
ABA
AC
B
BA
BB
C