java - 如何使用 Streams API 取消扁平化的层次结构

标签 java java-8 tree java-stream

如果我有一个仍然排序但扁平化的层次结构 - 我如何使用 Java Streams API 创建父/子结构?一个例子: 我要怎么走

-,A,Foo
A,A1,Alpha1
A,A2,Alpha2
-,B,Bar
B,B1,Bravo1
B,B2,Bravo2

-
  A
    A1,Alpha1
    A2,Alpha2
  B
    B1,Bravo1
    B2,Bravo2

一种简单的非流方式是跟踪父列并查看它是否已更改。

我已经尝试了各种 Collectors 和 groupingBy 方法,但还没有找到如何做。

List<Row> list = new ArrayList<>();
list.add(new Row("-", "A", "Root"));
list.add(new Row("A", "A1", "Alpha 1"));
list.add(new Row("A", "A2", "Alpha 2"));
list.add(new Row("-", "B", "Root"));
list.add(new Row("B", "B1", "Bravo 1"));
list.add(new Row("B", "B2", "Bravo 2"));

//Edit
Map<Row, List<Row>> tree;

tree = list.stream().collect(Collectors.groupingBy(???))

最佳答案

您可以为每个 Row 创建一个 Map,并按名称索引:

Map<String,Row> nodes = list.stream().collect(Collectors.toMap(Row::getName,Function.identity()));

getName() 是传递给 Row 构造函数的第二个属性。

现在您可以使用那个 Map 来构建树:

Map<Row,List<Row>> tree = list.stream().collect(Collectors.groupingBy(r->nodes.get(r.getParent())));

getParent() 是传递给 Row 构造函数的第一个属性。

这需要 Row 类正确覆盖 equalshashCode,这样两个 Row 实例将如果它们具有相同的名称,则被认为是相等的。

不过,您可能应该将根 Row 添加到您的输入 List 中。像这样的东西:

list.add(new Row(null, "-", "Root"));

编辑:

我用一个完整的 Row 类对其进行了测试(虽然我做了一些快捷方式),包括一个从根开始沿着每个级别的第一个子节点遍历树的示例:

class Row {
    String name;
    String parent;
    Row (String parent,String name,String something) {
        this.parent = parent;
        this.name = name;
    }
    public String getParent () {return parent;}
    public String getName () {return name;}

    public int hashCode () {return name.hashCode ();}
    public boolean equals (Object other) {
        return ((Row) other).name.equals (name);
    }
    public String toString ()
    {
        return name;
    }

    public static void main (String[] args)
    {
        List<Row> list = new ArrayList<>();
        list.add(new Row(null, "-", "Root"));
        list.add(new Row("-", "A", "Root"));
        list.add(new Row("A", "A1", "Alpha 1"));
        list.add(new Row("A1", "A11", "Alpha 11"));
        list.add(new Row("A", "A2", "Alpha 2"));
        list.add(new Row("-", "B", "Root"));
        list.add(new Row("B", "B1", "Bravo 1"));
        list.add(new Row("B", "B2", "Bravo 2"));
        Map<String,Row> nodes = 
            list.stream()
                .collect(Collectors.toMap(Row::getName,Function.identity()));
        Map<Row,List<Row>> tree = 
            list.stream()
                .filter(r->r.getParent()!= null)
                .collect(Collectors.groupingBy(r->nodes.get(r.getParent())));
        System.out.println (tree);
        Row root = nodes.get ("-");
        while (root != null) {
            System.out.print (root + " -> ");
            List<Row> children = tree.get (root);
            if (children != null && !children.isEmpty ()) {
                root = children.get (0);
            } else {
                root = null;
            }
        }
        System.out.println ();
    }
}

输出:

树:

{A1=[A11], A=[A1, A2], B=[B1, B2], -=[A, B]}

遍历:

- -> A -> A1 -> A11 -> 

关于java - 如何使用 Streams API 取消扁平化的层次结构,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54123464/

相关文章:

java - 如何使用在 thenCombineAsync 中返回 CompletionStage 的方法

Java 流 : How to avoid add null value in Collectors. toList()?

Javascript 判断一个文件夹是否是另一个文件夹的子文件夹

typescript - 在 typescript 中递归转换对象树的所有叶子

java - 异步服务中的 Spring 请求范围?通过 threadLocal 变量实现 ThreadScope,加上一个 AsyncAspect 来清理

gwt - 在 apache tomcat 8 上运行 GWT 应用程序

java - 如何使用 View 和过滤器避免 hibernate 中的嵌套查询

algorithm - 将一棵二叉树分成 k 个大小相似的部分

java - 按前 3 个字符进行流分组

java - 在 Java 中从父类调用子类构造函数