如何将树结构转换为java中的节点流

kwi*_*atz 8 tree-traversal java-stream

我想在Java8节点流中转换树

这是存储可以选择的数据的节点树

public class SelectTree<D> {

  private D data;

  private boolean selected = false;

  private SelectTree<D> parent;

  private final List<SelectTree<D>> children = new ArrayList<>();

  public SelectTree(D data, SelectTree<D> parent) {
    this.data = data;
    if (parent != null) {
      this.parent = parent;
      this.parent.getChildren().add(this);
    }
  }

  public D getData() {
    return data;
  }

  public void setData(D data) {
    this.data = data;
  }

  public boolean isSelected() {
    return selected;
  }

  public void setSelected(boolean selected) {
    this.selected = selected;
  }

  public SelectTree<D> getParent() {
    return parent;
  }

  public void setParent(SelectTree<D> parent) {
    this.parent = parent;
  }

  public List<SelectTree<D>> getChildren() {
    return children;
  }

  public boolean isRoot() {
    return this.getParent() == null;
  }

  public boolean isLeaf() {
    return this.getChildren() == null || this.getChildren().isEmpty();
  }
}
Run Code Online (Sandbox Code Playgroud)

我想获得所选数据的集合

我想做那样的事情:

  public static void main(String[] args) {
    SelectTree<Integer> root = generateTree();

    List<Integer> selectedData = root.stream()
            .peek(node -> System.out.println(node.getData()+": "+node.isSelected()))
            .filter(node-> node.isSelected())
            .map(node-> node.getData())
            .collect(Collectors.toList()) ;

    System.out.println("\nselectedData="+selectedData);
  }

  private static SelectTree<Integer> generateTree() {
    SelectTree<Integer> n1 = new SelectTree(1, null);
    SelectTree<Integer> n11 = new SelectTree(11, n1);
    SelectTree<Integer> n12 = new SelectTree(12, n1);
    n12.setSelected(true);
    SelectTree<Integer> n111 = new SelectTree(111, n11);
    n111.setSelected(true);
    SelectTree<Integer> n112 = new SelectTree(112, n11);
    SelectTree<Integer> n121 = new SelectTree(121, n12);
    SelectTree<Integer> n122 = new SelectTree(122, n12);
    return n1;
  }
Run Code Online (Sandbox Code Playgroud)

问题是找到实施,stream()我想我可以帮助一些人分享我的解决方案,我会知道是否有一些问题或更好的方法来做到这一点.

起初它是为了表面,TreeNode但我将问题概括为各种树木

Bas*_*ass 11

kwisatz答案的一小部分补充.

这个实现:

this.getChildren().stream()
        .map(SelectTree::stream)
        .reduce(Stream.of(this), Stream::concat);
Run Code Online (Sandbox Code Playgroud)

将更加渴望,即在流创建期间遍历整个层次结构.如果你的hirarchy很大,让我们说,你正在寻找一个匹配某个谓词的单个节点,你可能想要一个更懒惰的行为:

Stream.concat(Stream.of(this),
              this.getChildren().stream().flatMap(SelectTree::stream));
Run Code Online (Sandbox Code Playgroud)

在这种情况下,在流创建期间仅检索根节点的子节点,并且搜索节点不一定会导致遍历整个层次结构.

两种方法都将展示DFS迭代顺序.

  • 甚至[文档](https://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html#concat-java.util.stream.Stream-java.util.stream。 Stream-) 警告过度使用 `concat`:“*通过重复串联构造流时要小心。访问深度串联流的元素可能会导致深度调用链,甚至 StackOverflowException[sic].*” 因此 `reduce(Stream.of(this), Stream::concat)` 应该敲响警钟。在 Java 8 中,“flatMap”方法并不像应有的那么懒惰,但至少,它不容易出现堆栈溢出。在较新的版本中,它很懒。 (2认同)

kwi*_*atz 9

我发现这个实现stream()是一个DFS树遍历:

public class SelectTree<D> {

  //...

  public Stream<SelectTree<D>> stream() {
    if (this.isLeaf()) {
      return Stream.of(this);
    } else {
      return this.getChildren().stream()
                .map(child -> child.stream())
                .reduce(Stream.of(this), (s1, s2) -> Stream.concat(s1, s2));
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

如果你不能像primefaces TreeNode(org.primefaces.model.TreeNode)那样改变树实现,你可以在另一个类中定义一个方法:

  public Stream<TreeNode> stream(TreeNode parentNode) {
    if(parentNode.isLeaf()) {
      return Stream.of(parentNode);
    } else {
      return parentNode.getChildren().stream()
                .map(childNode -> stream(childNode))
                .reduce(Stream.of(parentNode), (s1, s2) -> Stream.concat(s1, s2)) ;
    }
  }
Run Code Online (Sandbox Code Playgroud)