从平面列表构建分层列表

Posted

技术标签:

【中文标题】从平面列表构建分层列表【英文标题】:Build hierarchical list from flat list 【发布时间】:2018-01-17 12:18:19 【问题描述】:

我有一个列表,其中包含由相同类型(类)表示的所有父母和孩子。我想从平面列表中生成分层列表。

下面是我的班级,代表父母和孩子。在这里,我需要根据 parentId 为父级映射子级。根级别(无父级)以 parentId 0 开头。

import java.util.ArrayList;
import java.util.List;

class Customers 

    private int id;
    private String name;
    private int parentId;
    private List<Customers> childs;

    public Customers(int id, String name, int parentId) 
        this(id, name, parentId, null);
    

    public Customers(int id, String name, int parentId, List<Customers> childs) 
        this.id = id;
        this.name = name;
        this.parentId = parentId;
        this.childs = childs;
    



public class Application 

    public static void main(String[] args) 
        //Input List
        Customers customer = new Customers(1, "Hel", 0);
        Customers customerChild1 = new Customers(2, "Hel-Child1", 1);
        Customers customerChild2 = new Customers(5, "Hel-Child2", 1);
        Customers customerInnerChild1 = new Customers(3, "Hel-InnerChild1", 2);
        Customers customerInnerChild2 = new Customers(4, "Hel-InnerChild2", 2);

        List<Customers> customers = new ArrayList();
        customers.add(customerInnerChild1);
        customers.add(customerInnerChild2);
        customers.add(customer);
        customers.add(customerChild1);
        customers.add(customerChild2);
    

    public static List<Customers> hierarchicalList(List<Customers> Customers) 
        return null;
    

我会将List&lt;Customer&gt; 作为输入传递给某个方法。该方法应返回具有父子关系的List&lt;Customer&gt;

我需要帮助以优化方式建立父子关系。

输入:

[
  
    "id": 1,
    "name": "Hel",
    "parentId": 0
  ,
  
    "id": 5,
    "name": "Hel-Child2",
    "parentId": 1
  ,
  
    "id": 4,
    "name": "Hel-InnerChild2",
    "parentId": 2
  ,
  
    "id": 3,
    "name": "Hel-InnerChild1",
    "parentId": 2
  ,
  
    "id": 2,
    "name": "Hel-Child1",
    "parentId": 1
  
]

预期输出:

[
  
    "id": 1,
    "name": "Hel",
    "parentId": 0,
    "childs": [
      
        "id": 2,
        "name": "Hel-Child1",
        "parentId": 1,
        "childs": [
          
            "id": 3,
            "name": "Hel-InnerChild1",
            "parentId": 2
          , 
            "id": 4,
            "name": "Hel-InnerChild2",
            "parentId": 2
          
        ]
      , 
        "id": 5,
        "name": "Hel-Child2",
        "parentId": 1
      
    ]
  
]

注意:输入列表未排序。

【问题讨论】:

"该方法应返回具有父子关系的 List。"我不明白你的意思。合并列表? 到目前为止你尝试过什么?也许这会让我们一瞥“分层列表”的含义。 @daniu,更新了我的问题。我什么都没试过。 @hellzone ,更新了我的问题,请检查。 我只是不明白你怎么知道 Hel-InnerChild1 和 Hel-InnerChild2 是 Hel-Child1 而不是 Hel-Child2 的孩子? 【参考方案1】:

如果你只需要检查谁是每个孩子的父母,你只需要过滤列表并为每个元素设置孩子。像这样的:

public List<Customer> getHierarchicalList(final List<Customer> originalList) 
    final List<Customer> copyList = new ArrayList<>(originalList);

    copyList.forEach(element -> 
        originalList
                .stream()
                .filter(parent -> parent.id == element.parentId)
                .findAny()
                .ifPresent(parent -> 
                    if (parent.childs == null) 
                        parent.childs = new ArrayList<>();
                    
                    parent.childs.add(element);
                    originalList.remove(element);
                );
    );
    return originalList;

【讨论】:

它会无限循环,你可以试试我的输入。 好吧,我没有在这里运行您的输入的无限循环...但是在将子项添加到 null childList 时出现 NPE...我已经更改了方法以避免NPE,但我鼓励您在构造函数中初始化子数组,因此它永远不会为 NULL。 我得到的异常:com.collections.Customers.toString(Application.java) 的 java.lang.StringBuilder.append(StringBuilder.java:136) 处的线程“main”java.lang.***Error 异常:58) 在 java.lang.String.valueOf(String.java:2994) 在 java.lang.StringBuilder.append(StringBuilder.java:131) 在 java.util.AbstractCollection.toString(AbstractCollection.java:462) 在 java .lang.String.valueOf(String.java:2994) 在 java.lang.StringBuilder.append(StringBuilder.java:131) 在 com.collections.Customers.toString(Application.java:58) 在 java.lang.String。 valueOf(String.java:2994) 好的,现在运行我的代码,我得到了与您在问题中建议的相同的输出。但即使是现在我也无法得到这个无限循环的人。我正在使用 IntelliJ IDEA 作为我的 IDE...如果您需要更多帮助,可以私下与我交谈,我很乐意提供帮助:rhuankarlus@gmail.com 当我尝试这段代码时,我只能得到 2 级递归 .. 没有进入 3 以上【参考方案2】:

这个类已经是分层的。

如果您打印父级,然后递归地打印子级,您将看到一棵树。

你期望会发生什么?

这只是你想如何迭代树的问题:深度优先还是广度优先?

您关于从平面列表到树的问题是关于解析平面列表。你如何区分平面列表中的父母和孩子?这应该给你一个关于如何进行的提示。

您需要一个工厂类,它接受客户列表、解析列表并返回客户的根实例及其在适当树结构中的子级。

【讨论】:

用实际输入预期输出更新了我的问题。你能检查一下吗? 应该很容易。遍历列表并检查每个条目。如果它有父 id,则将其添加到父列表中。应该只有一个客户没有父母。那是树的根。 这里我有 n 个父级的根级别和 n 个子级。所以,我正在寻找有效的好解决方案。我可能会迭代 100,000 个项目。 有人告诉我,一棵树只有一个根。如果您有多个根,则您有多个树,而不是一棵。【参考方案3】:

我想扩展Rhuan的答案,

通过使用Rhuan的代码,你会得到所有节点都在根层,只有一个具有正确内容和顺序的节点,通过删除错误的内容你会得到更好的结果:

public List<Customer> getHierarchicalList(final List<Customer> originalList) 
    final List<Customer> copyList = new ArrayList<>(originalList);

    copyList.forEach(element -> 
        originalList
                .stream()
                .filter(parent -> parent.id == element.parentId)
                .findAny()
                .ifPresent(parent -> 
                    if (parent.childs == null) 
                        parent.childs = new ArrayList<>();
                    
                    parent.childs.add(element);
                    /* originalList.remove(element); don't remove the content before completing the recursive */
                );
    );
    originalList.subList(1, originalList.size()).clear();
    return originalList;

注意子列表清除。

【讨论】:

【参考方案4】:

我也想扩展Rhuan的答案。

如果你的代码导致无限循环,你需要比较父元素和元素id,它们不应该等于parent.id != element.id

所以你的代码将是:

public List<Customer> getHierarchicalList(final List<Customer> originalList) 
    final List<Customer> copyList = new ArrayList<>(originalList);

    copyList.forEach(element -> 
        originalList
                .stream()
                .filter(parent -> parent.id == element.parentId && parent.id != element.id)
                .findAny()
                .ifPresent(parent -> 
                    if (parent.childs == null) 
                        parent.childs = new ArrayList<>();
                    
                    parent.childs.add(element);
                    originalList.remove(element);
                );
    );
    return originalList;

【讨论】:

以上是关于从平面列表构建分层列表的主要内容,如果未能解决你的问题,请参考以下文章

将分层平面数据(带 ParentID)转换为带缩进级别的排序平面列表的算法

如何针对分层对象列表动态构建和存储复杂的 linq 查询?

递归构建分层JSON树?

从分层数组Ruby生成平面数组的递归函数[关闭]

Swifty JSON:将 JSON 数组中的对象分层列表转换为分层 Swift 对象

获取分层表中子级列表的根元素