考虑我想使用Stream API遍历树状结构的某些节点(类似问题:[1],[2],[3])。想到的第一个实现将是:
abstract class Node {
abstract Collection<Node> getChildren();
final Stream<Node> stream() {
return Stream.concat(Stream.of(this), this.getChildren().stream().flatMap(Node::stream));
}
}
上述stream()
实施具有以下功能:
现在考虑我有一个很大的层次结构,getChildren()
操作成本很高,而且我正在寻找匹配某个谓词的任何 Node
:
final Node tree = ...;
final Predicate<Node> p = ...;
tree.stream().filter(p).findAny();
stream()
实施&#34;完全懒惰&#34;?如果节点本身已经与谓词匹配,我不希望查询节点的子节点。我正在寻找一个Stream.concat()
的懒惰版本,签名为(Stream<T> a, Supplier<Stream<T>> b)
。答案 0 :(得分:2)
很遗憾,我只能回答您的第一个问题。同样,flatMap
是您的朋友在这里。它使我们能够创建略有不同的concat
方法,该方法接受流Supplier
而不是仅流:
abstract class Node {
abstract Collection<Node> getChildren();
Stream<Node> lazyTraverse() {
return Node.concat(() -> Stream.of(this),
() -> getChildren().stream().flatMap(Node::lazyTraverse));
}
static Stream<Node> concat(Supplier<Stream<Node>> a, Supplier<Stream<Node>> b) {
return Stream.of(a, b).flatMap(Supplier::get);
}
}
一个更好的解决方案是,如果您可以用某种机制代替getChildren
,而该机制返回一个惰性Stream<Node>
,并且可以直接在原始树遍历算法中使用。惰性流比慢速吸收器更合适。
关于第二个问题的一些单词:
我不知道是否有使用流式API优雅地进行BFS遍历的算法,但我倾向于说“不”,因为BFS通常需要额外的内存来存储所有已访问但尚未遍历的节点
答案 1 :(得分:1)
您可以使用rdf:Statement
库和低级流支持原语。然后,您可以在节点上提供Spliterators
,它只会逐个使用节点。
Iterator
答案 2 :(得分:1)
有点难看,但这应该适用于Java 8:
public static <N> Stream<N> breadthFirst(N start, Function<? super N, Stream<N>> getChildren) {
final LinkedList<Stream<N>> generations = new LinkedList<>();
generations.add(Stream.of(start));
final Iterator<Stream<N>> genIterator = createIterator(generations::remove, () -> !generations.isEmpty());
return StreamSupport.stream(Spliterators.spliteratorUnknownSize(genIterator, Spliterator.ORDERED), false)
.flatMap(Function.identity())
.distinct() // avoids loops
.peek(n -> generations.add(getChildren.apply(n)));
}
public static <E> Iterator<E> createIterator(Supplier<E> supplier, BooleanSupplier hasNext) {
return new Iterator<E>() {
@Override
public boolean hasNext() {
return hasNext.getAsBoolean();
}
@Override
public E next() {
return supplier.get();
}
};
}
这里的想法是你需要挂起对后代的引用,所以我们创建一个列表来在处理时保存它们。使用Java 9,您将能够使用Stream.generate(generations::poll).takeWhile(Objects::nonNull)
替换自定义迭代器代码。