Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/314.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 在树结构上创建深度流_Java_Tree_Java 8_Java Stream - Fatal编程技术网

Java 在树结构上创建深度流

Java 在树结构上创建深度流,java,tree,java-8,java-stream,Java,Tree,Java 8,Java Stream,因此,我有一个基本的树结构,由树节点组成,这些树节点通过父节点和子节点引用链接到其他树节点。我想创建一个方法,返回一个从叶节点到根节点或从根到叶的流。我已经实现了这一点,但我正在寻找一个解决方案,其中创建的对象数量最少。最好没有。这是我的密码: public class TreeNode<TNode> { private TNode iValue; private TreeNode<TNode> iParentNode = null;

因此,我有一个基本的树结构,由树节点组成,这些树节点通过父节点和子节点引用链接到其他树节点。我想创建一个方法,返回一个从叶节点到根节点或从根到叶的流。我已经实现了这一点,但我正在寻找一个解决方案,其中创建的对象数量最少。最好没有。这是我的密码:

  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--);
           }

       }
   }
公共类树节点{
私人特诺德·伊瓦鲁;
私有树节点iParentNode=null;
private List iChildren=new ArrayList();
公共树节点(TNode值){
该值为(值,null);
}
私有TreeNode(TNode值,TreeNode父节点){
iValue=价值;
iParentNode=父节点;
}
公共流streamFromLeaf(){
返回StreamSupport.stream(Spliterators.spliteratorUnknownSize(新的LeafFirstIterator(this),Spliterator.SIZED),
假);
}
公共流streamFromRoot(){
返回StreamSupport.stream(Spliterators.spliteratorUnknownSize(新的RootFirstIterator(this),Spliterator.SIZED),
假);
}
公共TNode getValue(){
返回伊瓦鲁;
}
公共树节点getParent(){
返回iParentNode;
}
公共树节点addChild(TNode childValue){
TreeNode childNode=新的TreeNode(childValue,iNodeNameFunction,this);
添加(子节点);
返回子节点;
}
公共布尔isLeaf(){
返回iChildren.size()==0;
}
公共布尔值isRoot(){
返回iParentNode==null;
}
公共列表getChildren(){
返回iChildren;
}
类LeafFirstIterator实现了迭代器{
私有树节点;
LeafFirstIterator(TreeNode leafNode){
iNextNode=叶节点;
}
@凌驾
公共布尔hasNext(){
返回inxtnode!=null;
}
@凌驾
公共树节点next(){
三节点电流=不确定节点;
inxtNode=current.getParent();
回流;
}
}
类RootFirstIterator实现了迭代器{
私有列表索引节点=新的ArrayList();
私有不精确索引;
RootFirstIterator(TreeNode leafNode){
TreeNode currentNode=叶节点;
while(currentNode!=null){
添加(当前节点);
currentNode=currentNode.getParent();
}
iNextIndex=iNodes.size()-1;
}
@凌驾
公共布尔hasNext(){
返回不确定性指数>=0;
}
@凌驾
公共树节点next(){
返回iNodes.get(不精确索引--);
}
}
}
这很好,但我的“问题”是,每个流调用都会创建很多对象

  • StreamSupport.stream创建新的ReferencePipeline
  • Spliterators.spliteratorUnknownSize创建新迭代器Spliterator
  • 我创建自己的迭代器实现以传递给拆分器
  • RootFirstIterator创建新的ArrayList
流将被大量使用,因此我希望尽可能避免对象创建。在没有流的情况下迭代树结构是一件简单的事情。现在我只使用stream的map方法。我可以有一个方法,它接受一个消费者,迭代深度并为每个节点调用消费者,这样就不会创建任何对象,但我会失去所有流功能。看起来是这样的:

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);
}
public void iterateUp(消费者){
(这个,消费者);
}
公共静态无效向上(TreeNode节点,使用者){
if(node==null)
返回;
consumer.accept(节点);
doiteraup(node.getParent(),consumer);
}
从根开始向下迭代也同样简单

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


谢谢

不要使用流。使用您的备选方案,它有一个名称:the

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


如果您绝对必须拥有流,请让您的使用者收集列表中的节点,然后将其流化,或者将使用者连接到迭代器的
next()
方法(使用一些代码使其正常运行),并使用StreamSupport将其转换为流。

但是,我不同意您的性能问题,您的代码有简化的余地,这可能会解决一些您担心的副作用

您犯了一个常见的错误,即从
迭代器
实现开始,这很可能是因为
迭代器
接口很旧而且很有名。但是实现它很麻烦,如果直接实现
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;
    }
}

这不是必须的,但会非常好。我的另一个想法是将一个过滤器谓词、一个映射函数和一个使用者传递给迭代方法,我将得到类似于func的部分流,并且不会创建任何对象。但我觉得有点难看。将节点收集到列表并进行流式处理,我认为这与我的解决方案的对象创建非常相似,实际上可能更好一些。谢谢!好的指针。正如您所说的,由于迭代器更为常见,所以有一种倾向是使用迭代器。拆分器方法看起来确实更干净。我们将尝试一下,并更仔细地了解新的Java8内容。关于绩效。这段代码对延迟非常关键,为了避免延迟异常值,我们尽量不将GC驱动到太大的程度。
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();
}