JAVA将List转成Tree树形结构数据和深度优先遍历

引言:

在日常开发中,我们经常会遇到需要将数据库中返回的数据转成树形结构的数据返回,或者需要对转为树结构后的数据绑定层级关系再返回,比如需要统计当前节点下有多少个节点等,因此我们需要封装一个ListToTree的工具类和学会如何通过深度优先遍历数据。

数据准备:

先简单准备一下具有父子关系的数据。

java 复制代码
package data;
 
 
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;
    }
}
java 复制代码
package data;
 
import java.util.ArrayList;
import java.util.List;
 
public class SingData {
    public static List<OrgData> getData() {
        OrgData orgData1 = new OrgData();
        orgData1.setId("1");
        orgData1.setpId("root");
        orgData1.setOrgName("根节点A");
 
        OrgData orgData2 = new OrgData();
        orgData2.setId("2");
        orgData2.setpId("root");
        orgData2.setOrgName("根节点B");
 
        OrgData orgData3 = new OrgData();
        orgData3.setId("3");
        orgData3.setpId("1");
        orgData3.setOrgName("A目录");
 
        OrgData orgData4 = new OrgData();
        orgData4.setId("4");
        orgData4.setpId("2");
        orgData4.setOrgName("B目录");
 
        List<OrgData> list = new ArrayList<>();
        list.add(orgData1);
        list.add(orgData2);
        list.add(orgData3);
        list.add(orgData4);
 
        return list;
    }
}

正常情况下,我们都会选择封装一个将List转换为Tree的工具类,并且通过链式顺序LinkedList存储来遍历Tree数据,这里使用stream来实现,如下:

java 复制代码
package data;
 
import java.util.List;
 

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 +
                '}';
    }
}

执行结果:

620fc2d13f944d61888c2f0af8198ad2.png

对tree数据进行遍历,通过链表的形式:

java 复制代码
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;
 

public class Main {
    public static void main(String[] args) {
        // 模拟查询数据
        List<OrgData> orgDataList = SingData.getData();
        // 构建tree数据
        List<TreeBean> treeBean = ListToTreeUtil.toTree(orgDataList, "root");
        // 使用LinkedList实现链式队列,实现深度遍历
        LinkedList<TreeBean> stack = new LinkedList<>();
        stack.addAll(treeBean);
        while (!stack.isEmpty()) {
            // 从栈顶开始访问
            TreeBean pop = stack.peek();
            // Flag=1表示已经遍历过一次且该节点存在子节点
            if (pop.getFlag() == 1) {
                OrgData orgData =pop.getOrgData();
                List<TreeBean> children = pop.getChildren();
                // 获取子节点的节点名称,也可以进行其他的操作
                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出栈,当前节点已经统计完,出栈获取下一个栈顶peek
                stack.pop();
            } else {
                // flag为0表示未遍历,判断是否已经遍历到叶子节点(最底部)
                if (pop.getChildren()!= null && !pop.getChildren().isEmpty()) {
                    // 非叶子节点
                    pop.setFlag(1);
                    List<TreeBean> children = pop.getChildren();
                    for (TreeBean child : children) {
                        // 将叶子节点入栈,放到栈顶,实现深度遍历,next
                        stack.push(child);
                    }
                } else {
                    // 叶子节点直接出栈即可,cnt为本身
                    stack.pop();
                }
            }
        }
 
        // 遍历最终的数据
        for (OrgData orgData : orgDataList) {
            System.out.println(orgData.toString());
        }
    }
}

但是现在有一个问题,当我们响应的数据从OrgData换到其他类型的时候,这时候就需要封装成一个泛型类使得我们的tree数据生成类变成一个通用的,完整代码如下:

完整代码:JAVA将List转为Tree和深度优先遍历

java 复制代码
package data;
 
import java.util.List;
 

public interface Tree<T> {
    String getTreeId();
 
    String getTreePid();
 
    void setChild(List<T> list);
}
java 复制代码
package data;
 
 
import java.util.List;
 
/**
 * 实现Tree<OrgData>并定义响应类型为本身
 * 定义转为树的参数返回
*/
public class OrgData implements Tree<OrgData>{
    private String id;
 
    private String pId;
 
    private String orgName;
 
    // 转成树需要的参数
    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方法

java 复制代码
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;
 
/**
 * 将List转为Tree
 * <T extends Tree>告诉编译器有Tree的get方法
 *
 */
public class ListToTreeUtil {
    public static <T extends Tree> List<T> toTree(List<T> list, String root) {
        // 当根节点为null时,定义一个初始值,防止null
        String treeRoot = "treeRoot";
        String finalRoot = root;
        if (list.isEmpty()) {
            return new ArrayList<>();
        }
 
        // 构建Map数据
        // 根据pid分组,获取所有的子节点集合
        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 && !children.isEmpty()) {
                        data.setChild(children);
                    }
                })
                .filter(data -> data.getTreePid().equals(finalRoot))
                .collect(Collectors.toList());
    }
}

深度优先遍历

java 复制代码
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;
 

public class Main {
    public static void main(String[] args) {
        // 模拟查询数据
        List<OrgData> orgDataList = SingData.getData();
        // 构建tree数据
        List<OrgData> treeBean = ListToTreeUtil.toTree(orgDataList, "root");
        // 使用LinkedList实现链式队列,实现深度遍历
        LinkedList<OrgData> stack = new LinkedList<>();
        stack.addAll(treeBean);
        while (!stack.isEmpty()) {
            // 从栈顶开始访问
            OrgData pop = stack.peek();
            // Flag=1表示已经遍历过一次且该节点存在子节点
            if (pop.getFlag() == 1) {
                List<OrgData> children = pop.getChild();
                // 获取子节点的节点名称,也可以进行其他的操作
                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出栈,当前节点已经统计完,出栈获取下一个栈顶peek
                stack.pop();
            } else {
                // flag为0表示未遍历,判断是否已经遍历到叶子节点(最底部)
                if (pop.getChild()!= null && !pop.getChild().isEmpty()) {
                    // 非叶子节点
                    pop.setFlag(1);
                    List<OrgData> children = pop.getChild();
                    for (OrgData child : children) {
                        // 将叶子节点入栈,放到栈顶,实现深度遍历,next
                        stack.push(child);
                    }
                } else {
                    // 叶子节点直接出栈即可,cnt为本身
                    stack.pop();
                }
            }
        }
 
        // 遍历最终的数据
        for (OrgData orgData : orgDataList) {
            System.out.println(orgData.toString());
        }
    }
}
相关推荐
Abladol-aj20 分钟前
并发和并行的基础知识
java·linux·windows
清水白石00820 分钟前
从一个“支付状态不一致“的bug,看大型分布式系统的“隐藏杀机“
java·数据库·bug
吾日三省吾码6 小时前
JVM 性能调优
java
弗拉唐7 小时前
springBoot,mp,ssm整合案例
java·spring boot·mybatis
oi777 小时前
使用itextpdf进行pdf模版填充中文文本时部分字不显示问题
java·服务器
少说多做3438 小时前
Android 不同情况下使用 runOnUiThread
android·java
知兀8 小时前
Java的方法、基本和引用数据类型
java·笔记·黑马程序员
蓝黑20208 小时前
IntelliJ IDEA常用快捷键
java·ide·intellij-idea
Ysjt | 深8 小时前
C++多线程编程入门教程(优质版)
java·开发语言·jvm·c++