当前位置: 首页 > 知识库问答 >
问题:

在树结构上创建深度流

席言
2023-03-14

因此,我有一个基本的树结构,该结构由树节点组成,该树节点链接到具有父级和子级引用的其他树节点。我想创建一个方法,该方法返回一个 Stream,该流从叶节点流式传输到根节点或从根到叶。我已经实现了这个,但我正在寻找一种创建最少对象的解决方案。最好没有。这是我的代码

  public class TreeNode<TNode> {

      private TNode iValue;

      private TreeNode<TNode> iParentNode = null;

      private List<TreeNode<TNode>> iChildren = new ArrayList<>();

      public TreeNode(TNode value) {
          this(value, null);
      }

      private TreeNode(TNode value, TreeNode<TNode> parentNode) {
          iValue = value;
          iParentNode = parentNode;
      }

      public Stream<TreeNode<TNode>> streamFromLeaf() {
          return StreamSupport.stream(Spliterators.spliteratorUnknownSize(new LeafFirstIterator(this), Spliterator.SIZED),
            false);
      }

      public Stream<TreeNode<TNode>> streamFromRoot() {
          return StreamSupport.stream(Spliterators.spliteratorUnknownSize(new RootFirstIterator(this), Spliterator.SIZED),
            false);
      }

      public TNode getValue() {
          return iValue;
      }

      public TreeNode<TNode> getParent() {
          return iParentNode;
      }

      public TreeNode<TNode> addChild(TNode childValue) {
          TreeNode<TNode> childNode = new TreeNode<TNode>(childValue, iNodeNameFunction, this);
          iChildren.add(childNode);
          return childNode;
      }

      public boolean isLeaf() {
          return iChildren.size() == 0;
      }

      public boolean isRoot() {
          return iParentNode == null;
      }

      public List<TreeNode<TNode>> getChildren() {
          return iChildren;
      }

      class LeafFirstIterator implements Iterator<TreeNode<TNode>> {

          private TreeNode<TNode> iNextNode;

          LeafFirstIterator(TreeNode<TNode> leafNode) {
              iNextNode = leafNode;
          }

          @Override
          public boolean hasNext() {
              return iNextNode != null;
          }

          @Override
          public TreeNode<TNode> next() {
              TreeNode<TNode> current = iNextNode;
              iNextNode = current.getParent();
              return current;
          }

      }

      class RootFirstIterator implements Iterator<TreeNode<TNode>> {

          private List<TreeNode<TNode>> iNodes = new ArrayList<>();

          private int iNextIndex;

          RootFirstIterator(TreeNode<TNode> leafNode) {
              TreeNode<TNode> currentNode = leafNode;
              while (currentNode != null) {
                  iNodes.add(currentNode);
                  currentNode = currentNode.getParent();
              }
              iNextIndex = iNodes.size() - 1;
           }

           @Override
           public boolean hasNext() {
               return iNextIndex >= 0;
           }

           @Override
           public TreeNode<TNode> next() {
               return iNodes.get(iNextIndex--);
           }

       }
   }

这很好,但我的“问题”是,为每个流调用创建了很多对象。

  • StreamSupport.stream 创建新的 ReferencePipeline
  • Spliterators.spliteratorUnknownSize 创建新的 IteratorSpliterator
  • 我创建自己的迭代器实现以传递给Spliterator
  • RootFirstIterator 创建新的 ArrayList

流将被大量使用,所以我想尽可能避免创建对象。在没有流的情况下迭代树结构是一件简单的事情。现在我只使用流的映射方法。我可以只拥有一个接受消费者的方法,迭代深度并为每个节点调用消费者,不会创建任何对象,但我会失去所有流功能。看起来大概是这样的:

public void iterateUp(Consumer<TreeNode<TNode>> consumer) {
    doIterateUp(this, consumer);
}

public static <T> void doIterateUp(TreeNode<T> node, Consumer<TreeNode<T>> consumer) {
    if (node == null)
        return;
    consumer.accept(node);
    doIterateUp(node.getParent(), consumer);
}

从根开始向下迭代也一样简单。

对此有什么想法吗?我是不是走错路了?TreeNode应该实现或扩展一些接口/类吗?如果有什么不清楚的,请告诉我。

谢谢!

共有2个答案

韦安顺
2023-03-14

我不同意您对性能的担忧,但是,您的代码还有简化的空间,这可能会解决您的一些担忧作为副作用。

您犯了一个常见的错误,即从< code>Iterator实现开始,很可能是因为< code>Iterator接口很老而且广为人知。但是实现它很麻烦,而且如果您只是直接实现< code>Spliterator,则不需要将< code >迭代器包装在< code>Spliterator中,这只是一个很好的副作用:

public Stream<TreeNode<TNode>> streamFromLeaf() {
    return StreamSupport.stream(new LeafFirstSpliterator<>(this), false);
}
static class LeafFirstSpliterator<TNode>
extends Spliterators.AbstractSpliterator<TreeNode<TNode>> {
    private TreeNode<TNode> iNextNode;
    LeafFirstSpliterator(TreeNode<TNode> leafNode) {
        super(100, ORDERED|NONNULL);
        iNextNode = leafNode;
    }
    public boolean tryAdvance(Consumer<? super TreeNode<TNode>> action) {
        if(iNextNode==null) return false;
        action.accept(iNextNode);
        iNextNode=iNextNode.getParent();
        return true;
    }
}

对我来说,Spliterator 实现看起来更干净,至少它没什么好怕的,它可能允许更快的流遍历。您可以考虑重写 forEachRemaining 方法,因为它可以直接实现。

对于从根到叶的流,临时存储似乎是不可避免的,但如果是这样,请不要浪费时间进行低级编码,只需使用存储的内置流式处理功能:

public Stream<TreeNode<TNode>> streamFromRoot() {
    ArrayDeque<TreeNode<TNode>> deque = new ArrayDeque<>();
    for(TreeNode<TNode> n = this; n != null; n = n.getParent())
        deque.addFirst(n);
    return deque.stream();
}
长孙深
2023-03-14

不要使用流。使用您的替代方案,它有一个名称:访客模式。

流并不总是正确的方法,这是使用访问者模式的经典案例。

如果您一定要有一个流,让您的使用者收集列表中的节点,然后对其进行流处理,或者将使用者连接到迭代器的< code>next()方法(使用一些代码使其行为正常),并使用StreamSupport将其转换为流。

 类似资料:
  • 问题内容: 我有一个带有Node的图类,其中每个Node可以连接到其他节点: 我想复制整个图。第一次尝试,我尝试制作一个类似以下的复制构造函数: 因此,深度复制图形将是: 但这不起作用,因为这破坏了节点之间的连接关系。我想知道是否有人建议以一种简单的方式做到这一点?谢谢。 问题答案: 问题是您需要复制节点的身份,而不仅仅是节点的值。具体来说,当您复制某个节点时,您需要处理其所指节点的身份。这意味着

  • 我不确定我的问题是出在代码的深度复制部分,还是我在将元素添加到原始列表时犯了错误。到目前为止我所掌握的是: ...其中与此相关联的测试用例是:

  • 我试图创建一个JSON来表示任意深度和广度的层次结构,例如生物: 我想把它转换成这样的JSON: 这在博士后中可行吗? 到目前为止我已经试过了 但它失败了 有没有办法解决这个问题,或者其他解决方案可以让我成为我的好树?

  • 问题内容: 我是Java的新手,我试图找到一种方法来在C语言中存储诸如结构之类的信息。例如,说我想让一名程序雇用员工。它将从用户那里获得一个名字,姓氏和ID号并将其存储起来。然后,用户可以根据条件查看该信息(例如,如果数据库有多于1名员工)。有没有人建议这样做的最佳方法? 问题答案: C中的结构就像Java中的类一样,功能更强大,因为Java中的类可以包含方法,而C ++可以。您创建一个新类。例如

  • 主要内容:一个 XML 文档实例,XML 文档形成一种树结构,实例:,XML 文档实例XML 文档形成了一种树结构,它从"根部"开始,然后扩展到"枝叶"。 一个 XML 文档实例 XML 文档使用简单的具有自我描述性的语法: <?xml version="1.0" encoding="UTF-8"?> <note> <to>Tove</to> <from>Jani</from> <heading>Reminder</heading> <body>Don't forget me th

  • NowCoder 题目描述 从根结点到叶结点依次经过的结点(含根、叶结点)形成树的一条路径,最长路径的长度为树的深度。 解题思路 // java public int TreeDepth(TreeNode root) { return root == null ? 0 : 1 + Math.max(TreeDepth(root.left), TreeDepth(root.right));