【问题标题】:How to convert flat list to nested tree in java with a nested set model?java - 如何使用嵌套集模型将平面列表转换为Java中的嵌套树?
【发布时间】:2021-05-06 00:40:16
【问题描述】:

我正在使用nested set model 在我的 postgres 数据库中存储类别层次结构。我能够查询树以获取给定的类别 ID,并查看其所有子项及其所在的深度。该查询的响应如下所示:

[
    {
        "id": "07e0b6c2-49cd-440b-a900-0f3d7ab88022",
        "categoryId": "80d15a99-9e42-4b72-b44b-0b222ca5173e",
        "name": "Root",
        "lft": 1,
        "rgt": 18,
        "depth": 0
    },
    {
        "id": "ae53be00-c312-4cd5-a6b2-6baeaf760577",
        "categoryId": "9b8bca09-2447-494c-be0d-0b3af7d30671",
        "name": "Cat A",
        "lft": 2,
        "rgt": 9,
        "depth": 1
    },
    {
        "id": "0a5d4b90-29b9-4c50-a436-d129dc6983ea",
        "categoryId": "d06a143b-523e-4136-8a17-1049abbf76f4",
        "name": "Cat B",
        "lft": 3,
        "rgt": 4,
        "depth": 2
    },
    {
        "id": "11421455-abc0-464c-8bd0-e2d79302c270",
        "categoryId": "5af63d5b-f480-4620-8393-f4b93f7972e0",
        "name": "Cat D",
        "lft": 5,
        "rgt": 6,
        "depth": 2
    },
    {
        "id": "4463dbce-a2bf-42fe-a864-59309ba54d22",
        "categoryId": "21191930-a5b9-4868-883f-3798f29d70a3",
        "name": "Cat E",
        "lft": 7,
        "rgt": 8,
        "depth": 2
    },
    {
        "id": "0f40e7a0-e6eb-44a4-a9bd-b61512daa236",
        "categoryId": "34b127e8-7a8f-40b3-9b7e-63c8d507cc7b",
        "name": "Cat F",
        "lft": 10,
        "rgt": 11,
        "depth": 1
    },
    {
        "id": "87e9991e-085c-47a5-8357-79c0e467b8ec",
        "categoryId": "dfbbaac7-dda3-4f34-a787-183803f8e6fa",
        "name": "Cat G",
        "lft": 12,
        "rgt": 17,
        "depth": 1
    },
    {
        "id": "8a95b0ab-cf74-4083-9d17-40e70468350a",
        "categoryId": "f7f04485-d089-4a5d-98cd-20b0abeba8fc",
        "name": "Cat H",
        "lft": 13,
        "rgt": 14,
        "depth": 2
    },
    {
        "id": "dccee476-af73-4eb6-a595-f862984d4af6",
        "categoryId": "0eb165ec-0347-4336-8fc2-35c124bf26f2",
        "name": "Cat I",
        "lft": 15,
        "rgt": 16,
        "depth": 2
    }
]

您会注意到树已经变平了。我正在尝试将上述结构放入一个嵌套的树结构中,我可以将其作为 JSON 返回到我的 UI。理想情况下看起来像这样(为简洁起见,省略了数据):

[
    {
        "name": "Cat A",
        "children": [
            {
                "name": "Cat B",
                "children": [
                    {
                        "name": "Cat C"
                    },
                    {
                        "name": "Cat D"
                    }
                ]
            },
            {
                "name": "Cat E",
                "children": [
                    {
                        "name": "Cat F"
                    }
                ]
            }
        ]
    }
]

我尝试了很多选项,最近的选项是将原始列表流式传输到地图中,然后根据每个节点所在的深度收集其值。这并不能完全满足我的要求,因为它将处于同一级别但不一定属于同一棵树的节点分组。

List<List<CategoryTree>> listOfLists = new ArrayList<>(
                categoryTreeList.stream()
                        .collect(Collectors.groupingBy(CategoryTree::getDepth))
                        .values());

似乎我应该尝试关闭 lftrgt 值,以确定给定列表中的节点是否有任何子节点,这些子节点将由大于值 1 的传播指示。

我该如何解决这个问题?

【问题讨论】:

    标签: java tree java-stream hierarchy


    【解决方案1】:

    我建议您创建一个保存结构数据的类,而不是尝试使用原始地图。

    class Node {
        private final String id;
        private final String category;
        private Node left = null;
        private Node right = null;
    
        public Node(String id, String category) {
            this.id = id;
            this.category = category;
        }
    
        // left and right setters
    }
    

    您需要分两遍构建结构。

    首先将创建节点并将它们添加到列表中,以便可以使用它们的索引。

    List<Node> nodes = new ArrayList<>();
    for (row: queryResult) {
        nodes.add(new Node(row.id(), row.category()));
    }
    

    在第二遍中,您构建结构:

    int i = 0;
    for (row: queryResult) {
        Node node = nodes.get(i++);
        if (row.lft())
            node.setLeft(nodes.get(row.lft()));
        if (row.rgt())
            node.setRight(nodes.get(row.rgt()));
    }
    

    有一些方法可以一次性完成,然后解析前向引用,但我真的怀疑这是否值得。

    如果您的根节点不能保证是列表中的第一个,那么您需要在列表中查找一个不是任何其他节点的子节点的节点。

    实际上没有必要存储或使用深度,除非您特别需要它来做其他事情。即便如此,最好还是派生它(通过维护 parent 字段并沿层次结构向上走),除非保证结构是静态的。

    转换为 json 最好使用 gson 之类的库,然后为您的类使用一个自定义转换器,该转换器使用递归来转换整个结构。

    【讨论】:

    • 当这段代码运行时,结构不会被列表中的最后一个节点覆盖吗? Node node = nodes.get(i++);?
    • 该命令只是与查询结果中的行并行遍历列表中的节点。它不会改变结构。
    猜你喜欢
    • 2012-06-03
    • 1970-01-01
    • 2012-12-08
    • 1970-01-01
    • 2020-12-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多