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

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

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

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;
    }
}
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来实现,如下:

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数据进行遍历,通过链表的形式:

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和深度优先遍历

package data;
 
import java.util.List;
 

public interface Tree<T> {
    String getTreeId();
 
    String getTreePid();
 
    void setChild(List<T> list);
}
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方法

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());
    }
}

深度优先遍历

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());
        }
    }
}
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
深度优先遍历是一种用于遍历图的算法。它的基本思想是从图的某个顶点开始,沿着一条路径一直走到底,然后回溯到上一个顶点,再沿着另一条路径继续探索,直到所有的顶点都被访问过为止。深度优先遍历可以用递归或者栈来实现。 在邻接矩阵存储图的深度优先遍历过程中,首先我们需要定义一个数组来标记哪些顶点已经被访问过。然后从起始顶点开始,将其标记为已访问,并将其加入到一个栈中。接下来,从栈中取出一个顶点,访问它的邻接顶点中尚未被访问过的顶点,并将这些顶点加入到栈中。不断重复这个过程,直到栈为空为止。通过这种方式,我们可以按照深度优先的顺序遍历整个图。 需要注意的是,由于数据输入的顺序不同,建立的邻接表可能不同,因此深度优先遍历的输出结果可能不唯一。同时,你可以通过加入计数器和记录步骤的方式来获得深度优先遍历的结果和步骤。 具体的C语言编程实现可以参考以上引用中提到的代码和方法。这样,在读取数据后,你就可以使用深度优先遍历算法来遍历图,并得到所需的结果和步骤。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *3* [超详细C语言版数据结构:图的深度优先遍历(推荐收藏)](https://blog.csdn.net/lucky51222/article/details/118224395)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* [数据结构——图的DFS(深度优先遍历)- C语言代码实现](https://blog.csdn.net/weixin_45954730/article/details/123402628)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

princeAladdin

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值