使用Java 8 Streams汇总树节点

时间:2017-05-25 12:22:07

标签: recursion java-8 java-stream

是否可以使用Java 8流来汇总树的节点,如果可能的话,可以在一个班轮中使用?

这是一个节点类

public class Node
{   
private int nodeNum;    
ArrayList<Node> children = new ArrayList<>();

public Node(int num)
{
    this.nodeNum = num;
}

public int getNodeNum()
{
    return nodeNum;
}

public boolean addNode(Node node)
{
    return children.add(node);
}

public ArrayList<Node> getNodes()
{
    return this.children;
}
}

解决这个问题的正常方法是使用递归并总结节点,如下面的代码。

int getNodeSum(Node node)
{
    int total = 0;

    if(node.children.isEmpty())
        return node.getNodeNum();
    else
    {
        for(Node tempNode:node.children)
        {
            total+= getNodeSum(tempNode);
        }
        return total+node.getNodeNum();
    }
}

我们可以使用流来总结直接子节点,但我没有得到如何深入移动并使用Streams递归执行。 此代码仅将问题解决到单个级别。有什么想法吗?

total = list.stream().filter(Node -> node.children.isEmpty()).map(Node:: getNodeNum).reduce(node.getNodeNum(), (a,b) -> a+b);

2 个答案:

答案 0 :(得分:3)

您的问题的一个解决方案是使用递归和Stream.flatMap

首先,您需要将以下帮助方法添加到Node类:

public Stream<Node> allChildren() {
    return Stream.concat(
        Stream.of(this), 
        this.children.stream().flatMap(Node::allChildren)); // recursion here
}

返回Stream<Node>,其元素是此节点及其所有后代节点。

然后,您可以按如下方式重写getNodeSum方法:

int getNodeSum(Node node) {
    return node.allChildren()
        .mapToInt(Node::getNodeNum)
        .sum();
}

这使用上面定义的Node.allChildren方法以及Stream.mapToIntIntStream.sum方法来计算总和。

或者,您可以在Function<Node, Stream<Node>> descendants类中使用Node属性来执行递归:

private Function<Node, Stream<Node>> descendants =
    node -> Stream.concat(
        Stream.of(node),
        node.children.stream()
            .flatMap(this.descendants)); // recursion here: function invoked again

这是递归lambda表达式,因为您定义的函数位于=符号的两侧。这种lambda表达式只允许作为类的属性,即你不能将递归的lambda表达式赋给局部变量。

使用该递归函数,您可以按如下方式重写allChildren方法:

public Stream<Node> allChildren() {
    return descendants.apply(this);
}

最后,您的getNodeSum方法的代码与之前的版本相同:

int getNodeSum(Node node) {
    return node.allChildren()
        .mapToInt(Node::getNodeNum)
        .sum();
}

注意:虽然这种方法可能对某些人有吸引力,但它可能有一些缺点,即现在Node类的每个实例都具有descendants属性,尽管根本不需要。您可以通过使用此Tree类将此递归函数作为属性来绕过这一点,并且Node是内部类(删除了descendants属性)。

答案 1 :(得分:1)

您需要为Node类添加recusive方法,它将加入子流

public Stream<Node> recursiveConcat() {
    return Stream.concat(
        Stream.of(this),
        children.stream().flatMap(Node::recursiveConcat));
}

然后做 -

root.recusiveConcat().mapToInt(Node::getNodeNum).sum()

整个代码

public class Node {

    private int nodeNum;
    ArrayList<Node> children = new ArrayList<>();

    public Node(int num) {
        this.nodeNum = num;
    }

    public int getNodeNum() {
        return nodeNum;
    }

    public boolean addNode(Node node) {
        return children.add(node);
    }

    public ArrayList<Node> getNodes() {
        return this.children;
    }

    public Stream<Node> recursiveConcat() {
        return Stream.concat(
                Stream.of(this),
                children.stream().flatMap(Node::recursiveConcat));
    }
}


Node root = new Node(1);
Node node1 = new Node(2);
Node node2 = new Node(3);
Node node3 = new Node(4);
node2.addNode(node3);
node1.addNode(node2);
root.addNode(node1);
System.out.println(root.recursiveConcat().mapToInt(Node::getNodeNum).sum());