JAVA converts List into Tree structure data and depth-first traversal

Introduction:

In daily development, we often encounter the need to convert the data returned from the database into a tree structure, or need to bind the hierarchical relationship of the data converted into the tree structure and then return it. For example, we need to count the number of data under the current node. How many nodes, etc., so we need to encapsulate a ListToTree tool class and learn how to traverse the data through depth first.

Data preparation:

First, briefly prepare the data with a parent-child relationship.

package data;


/**
* @author sinder
* @date 2023/11/8 21:40
*/
public class OrgData {
    private String id;

    private String pId;

    private String orgName;

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getpId() {
        return pId;
    }

    public void setpId(String pId) {
        this.pId = pId;
    }

    public String getOrgName() {
        return orgName;
    }

    public void setOrgName(String orgName) {
        this.orgName = orgName;
    }
}
package data;

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

/**
 * Generate data and simulate database queries
 * @author sinder
 * @date 2023/11/8 22:17
 */
public class SingData {
    public static List<OrgData> getData() {
        OrgData orgData1 = new OrgData();
        orgData1.setId("1");
        orgData1.setpId("root");
        orgData1.setOrgName("Root Node A");

        OrgData orgData2 = new OrgData();
        orgData2.setId("2");
        orgData2.setpId("root");
        orgData2.setOrgName("Root Node B");

        OrgData orgData3 = new OrgData();
        orgData3.setId("3");
        orgData3.setpId("1");
        orgData3.setOrgName("A directory");

        OrgData orgData4 = new OrgData();
        orgData4.setId("4");
        orgData4.setpId("2");
        orgData4.setOrgName("B directory");

        List<OrgData> list = new ArrayList<>();
        list.add(orgData1);
        list.add(orgData2);
        list.add(orgData3);
        list.add(orgData4);

        return list;
    }
}

Under normal circumstances, we will choose to encapsulate a tool class that converts List to Tree, and traverses Tree data through chained sequential LinkedList storage. Here we use stream to implement it, as follows:

package data;

import java.util.List;

/**
 * Build tree data
 * @author sinder
 * @date 2023/11/8 22:30
 */
public class TreeBean {
    private String treeId;

    private String treePid;

    private String orgName;

    private Integer flag = 0;

    private List<TreeBean> children;

    private OrgData orgData;

    public String getTreeId() {
        return treeId;
    }

    public void setTreeId(String treeId) {
        this.treeId = treeId;
    }

    public String getOrgName() {
        return orgName;
    }

    public void setOrgName(String orgName) {
        this.orgName = orgName;
    }

    public String getTreePid() {
        return treePid;
    }

    public void setTreePid(String treePid) {
        this.treePid = treePid;
    }

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

    public void setChildren(List<TreeBean> children) {
        this.children = children;
    }

    public OrgData getOrgData() {
        return orgData;
    }

    public void setOrgData(OrgData orgData) {
        this.orgData = orgData;
    }

    public Integer getFlag() {
        return flag;
    }

    public void setFlag(Integer flag) {
        this.flag = flag;
    }

    @Override
    public String toString() {
        return "TreeBean{" +
                "treeId='" + treeId + '\'' +
                ", treePid='" + treePid + '\'' +
                ", orgName='" + orgName + '\'' +
                ", children=" + children +
                '}';
    }
}
package utils;

import data.OrgData;
import data.SingData;
import data.TreeBean;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * Convert List to Tree
 *
 * @author sinder
 * @date 2023/11/8 22:28
 */
public class ListToTreeUtil {
    public static List<TreeBean> toTree(List<OrgData> list, String root) {
        if (list.isEmpty()) {
            return new ArrayList<>();
        }
        // Build data
        List<TreeBean> treeBeans = list.stream().map(item -> {
            TreeBean treeBean = new TreeBean();
            treeBean.setTreeId(item.getId());
            treeBean.setTreePid(item.getpId());
            treeBean.setOrgName(item.getOrgName());
            return treeBean;
        }).collect(Collectors.toList());

        //Construct Map data
        Map<String, List<TreeBean>> treeMap = treeBeans.stream().collect(Collectors.groupingBy(item -> {
            if (item.getTreePid().isEmpty()) {
                item.setTreePid(root);
            }
            return item.getTreePid();
        }));

        //Group data
        return treeBeans.stream().peek(data -> {
            List<TreeBean> children = treeMap.get(data.getTreeId());
            if (children != null & amp; & amp; !children.isEmpty()) {
                data.setChildren(children);
            }
        }).filter(data -> data.getTreePid().equals(root)).collect(Collectors.toList());
    }
}

Results of the:

620fc2d13f944d61888c2f0af8198ad2.png

Traverse the tree data in the form of a linked list:

import data.OrgData;
import data.SingData;
import data.TreeBean;
import utils.ListToTreeUtil;

import java.util.LinkedList;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @author sinder
 * @date 2023/11/8 20:44
 */
public class Main {
    public static void main(String[] args) {
        // Simulate query data
        List<OrgData> orgDataList = SingData.getData();
        //Construct tree data
        List<TreeBean> treeBean = ListToTreeUtil.toTree(orgDataList, "root");
        //Use LinkedList to implement chained queue and achieve deep traversal
        LinkedList<TreeBean> stack = new LinkedList<>();
        stack.addAll(treeBean);
        while (!stack.isEmpty()) {
            //Start accessing from the top of the stack
            TreeBean pop = stack.peek();
            // Flag=1 indicates that it has been traversed once and the node has child nodes
            if (pop.getFlag() == 1) {
                OrgData orgData =pop.getOrgData();
                List<TreeBean> children = pop.getChildren();
                // Get the node name of the child node, and you can also perform other operations
                List<String> collect = children.stream().map(TreeBean::getOrgName).collect(Collectors.toList());
                StringBuilder builder = new StringBuilder();
                for (String s : collect) {
                    builder.append(s);
                    builder.append(">");
                }
                pop.setOrgName(builder.toString());
                orgData.setOrgName(pop.getOrgName());
                // Pop off the stack. The current node has been counted. Pop off the stack to get the next peek at the top of the stack.
                stack.pop();
            } else {
                // The flag is 0, indicating that it has not been traversed. Determine whether the leaf node (the bottom) has been traversed.
                if (pop.getChildren()!= null & amp; & amp; !pop.getChildren().isEmpty()) {
                    // non-leaf nodes
                    pop.setFlag(1);
                    List<TreeBean> children = pop.getChildren();
                    for (TreeBean child : children) {
                        // Push the leaf node into the stack and put it on the top of the stack to achieve depth traversal, next
                        stack.push(child);
                    }
                } else {
                    //The leaf node can be popped directly from the stack, and cnt is itself
                    stack.pop();
                }
            }
        }

        // Traverse the final data
        for (OrgData orgData : orgDataList) {
            System.out.println(orgData.toString());
        }
    }
}

1295308cbbed42f1a1e5d461a1056e2c.png

But now there is a problem. When the data we respond to changes from OrgData to other types, we need to encapsulate it into a generic class to make our tree data generation class a universal one. The complete code is as follows:

Complete code: JAVA converts List to Tree and depth-first traversal

package data;

import java.util.List;

/**
 * Define an interface so that each response data entity can implement it
 * @author sinder
 * @date 2023/11/9 0:31
 */
public interface Tree<T> {
    String getTreeId();

    String getTreePid();

    void setChild(List<T> list);
}
package data;


import java.util.List;

/**
 * Implement Tree<OrgData> and define the response type as itself
 * Define the parameters returned to be converted into a tree
* @author sinder
* @date 2023/11/8 21:40
*/
public class OrgData implements Tree<OrgData>{
    private String id;

    private String pId;

    private String orgName;

    // Parameters required to convert to tree
    private Integer flag = 0;

    private List<OrgData> child;

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getpId() {
        return pId;
    }

    public void setpId(String pId) {
        this.pId = pId;
    }

    public String getOrgName() {
        return orgName;
    }

    public void setOrgName(String orgName) {
        this.orgName = orgName;
    }

    public Integer getFlag() {
        return flag;
    }

    public void setFlag(Integer flag) {
        this.flag = flag;
    }

    public List<OrgData> getChild() {
        return child;
    }

    @Override
    public String toString() {
        return "OrgData{" +
                "id='" + id + '\'' +
                ", pId='" + pId + '\'' +
                ", orgName='" + orgName + '\'' +
                '}';
    }

    @Override
    public String getTreeId() {
        return id;
    }

    @Override
    public String getTreePid() {
        return pId;
    }

    @Override
    public void setChild(List<OrgData> list) {
        this.child = list;
    }
}

ListToTree method

package utils;

import data.OrgData;
import data.SingData;
import data.Tree;
import data.TreeBean;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * Convert List to Tree
 * <T extends Tree> tells the compiler that there is a get method of Tree
 *
 * @author sinder
 * @date 2023/11/8 22:28
 */
public class ListToTreeUtil {
    public static <T extends Tree> List<T> toTree(List<T> list, String root) {
        // When the root node is null, define an initial value to prevent null
        String treeRoot = "treeRoot";
        String finalRoot = root;
        if (list.isEmpty()) {
            return new ArrayList<>();
        }

        //Construct Map data
        //Group by pid and get all child node sets
        Map<String, List<T>> childMap =
                list.stream()
                        .collect(Collectors.groupingBy(item -> {
                            String treePid = item.getTreePid();
                            if (treePid == null) {
                                treePid = treeRoot;
                            }
                            return treePid;
                        }));
        return list.stream().peek(
                data -> {
                    List<T> children = childMap.get(data.getTreeId());
                    if (children != null & amp; & amp; !children.isEmpty()) {
                        data.setChild(children);
                    }
                })
                .filter(data -> data.getTreePid().equals(finalRoot))
                .collect(Collectors.toList());
    }
}

Depth-first traversal

import data.OrgData;
import data.SingData;
import data.TreeBean;
import utils.ListToTreeUtil;

import java.util.LinkedList;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @author sinder
 * @date 2023/11/8 20:44
 */
public class Main {
    public static void main(String[] args) {
        // Simulate query data
        List<OrgData> orgDataList = SingData.getData();
        //Construct tree data
        List<OrgData> treeBean = ListToTreeUtil.toTree(orgDataList, "root");
        //Use LinkedList to implement chained queue and achieve deep traversal
        LinkedList<OrgData> stack = new LinkedList<>();
        stack.addAll(treeBean);
        while (!stack.isEmpty()) {
            //Start accessing from the top of the stack
            OrgData pop = stack.peek();
            // Flag=1 indicates that it has been traversed once and the node has child nodes
            if (pop.getFlag() == 1) {
                List<OrgData> children = pop.getChild();
                // Get the node name of the child node, and you can also perform other operations
                List<String> collect = children.stream().map(OrgData::getOrgName).collect(Collectors.toList());
                StringBuilder builder = new StringBuilder();
                for (String s : collect) {
                    builder.append(s);
                    builder.append(">");
                }
                pop.setOrgName(builder.toString());
                // Pop off the stack. The current node has been counted. Pop off the stack to get the next peek at the top of the stack.
                stack.pop();
            } else {
                // The flag is 0, indicating that it has not been traversed. Determine whether the leaf node (the bottom) has been traversed.
                if (pop.getChild()!= null & amp; & amp; !pop.getChild().isEmpty()) {
                    // non-leaf nodes
                    pop.setFlag(1);
                    List<OrgData> children = pop.getChild();
                    for (OrgData child : children) {
                        // Push the leaf node into the stack and put it on the top of the stack to achieve depth traversal, next
                        stack.push(child);
                    }
                } else {
                    //The leaf node can be popped directly from the stack, and cnt is itself
                    stack.pop();
                }
            }
        }

        // Traverse the final data
        for (OrgData orgData : orgDataList) {
            System.out.println(orgData.toString());
        }
    }
}

7e6b1547ec5a47c0bc8b4d6f340fc78f.png

The article mainly talks about the tree data generation strategy and the in-depth traversal of tree data. Overall, a tree data is constructed first. In order to be compatible with the traversal after converting multiple Object data into trees, generics are used and the Tree interface is defined. , providing getid, getPid, setChild and other operation methods. After the entity class implements the Tree interface, it can use the corresponding methods to operate the ip and pid related to the parent-child relationship and the child nodes to build related tree data. This is also convenient in Using LinkedList to deeply traverse the tree can more flexibly operate the data of parent and child nodes. Deep traversal is mainly achieved by popping and pushing the stack, traversing all the way from the parent node to the leaf node before stopping.

The article is only a reference example. For more and better implementation methods, please leave comments!