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

引言:

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

数据准备:

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

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;

/**
 * 生成数据,模拟数据库查询
 * @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("根节点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数据,这里使用steam来实现,如下:

package data;

import java.util.List;

/**
 * 构建tree数据
 * @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;

/**
 * 将List转为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<>();
        }
        // 构建数据
        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());

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

        // 将数据进行分组
        return treeBeans.stream().peek(data -> {
            List<TreeBean> children = treeMap.get(data.getTreeId());
            if (children != null && !children.isEmpty()) {
                data.setChildren(children);
            }
        }).filter(data -> data.getTreePid().equals(root)).collect(Collectors.toList());
    }
}

执行结果:

对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;

/**
 * @author sinder
 * @date 2023/11/8 20:44
 */
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;

/**
 * 定义一个接口,使得每一个响应的数据实体来实现
 * @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;

/**
 * 实现Tree<OrgData>并定义响应类型为本身
 * 定义转为树的参数返回
* @author sinder
* @date 2023/11/8 21:40
*/
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方法
 *
 * @author sinder
 * @date 2023/11/8 22:28
 */
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;

/**
 * @author sinder
 * @date 2023/11/8 20:44
 */
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());
        }
    }
}

        文章主要讲了tree数据的生成策略和对tree数据的深度遍历,整体上先构建出一个tree数据,为了能兼容多Object数据转为tree之后的遍历,因此使用了泛型,并且定义了Tree接口,提供了getid、getPid、setChild等操作方法,实体类在实现了Tree接口后就可以使用相应的方法来操作父子关系相关的ip和pid以及子节点来构建相关的tree数据,这也方便的在使用LinkedList深度遍历tree能更加灵活的操作父子节点的数据,主要通过出栈入栈来实现深度遍历,一路从父节点一直遍历到叶子节点才停止。

文章仅为参考示例,更多更好的实现方式欢迎留言评论呀!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/125972.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Linux常见指令:从基础到理论

前言 目录 前言 1. find指令 拓展 2. grep指令 拓展 sort指令 uniq指令 wc指令 3. zip/unzip指令 4. tar指令 5. uname指令 拓展 6. Linux常用热键 7. 关机 8. rz指令 拓展 scp指令 9. shell命令以及运行原理 Linux常见指令是使用Linux系统时必不可少的一部分。通过掌握…

四种常见分布式限流算法实现!

转载&#xff1a;四种常见分布式限流算法实现&#xff01; - 知乎 大家好&#xff0c;我是老三&#xff0c;最近公司在搞年终大促&#xff0c;随着各种营销活动“组合拳”打出&#xff0c;进站流量时不时会有一个小波峰&#xff0c;一般情况下&#xff0c;当然是流量越多越好&…

maven 下载和安装

点击进入Maven下载网址 Maven – Download Apache Maven Maven详细下载列表 Index of /dist/maven/maven-3 本地仓库配置文件 配置环境变量 idea编辑器配置 maven Javajdk配置 字节码版本是否11

基于React使用swiperjs实现竖向滚动自动轮播

很多文章&#xff0c;都只提供了js部分&#xff0c;包括官方的文档也只有js部分&#xff0c;如果css设置不正确&#xff0c;会导致轮播图不自动播放。 使用的swiper版本&#xff1a;v11.0.3 文档 https://swiperjs.com/get-startedhttps://swiperjs.com/react 实现效果 使…

华为云交换数据空间 EDS:“可信、可控、可证”能力实现你的数据你做主

文章目录 前言一、数据安全流通价值的必要性和紧迫性1.1、交换数据空间&#xff08;EDS&#xff09;背景1.2、《数字中国建设整体布局规划》1.3、数据流通成为制约数据要素价值释放的瓶颈 二、华为云 EDS 解决方案介绍2.1、构建可控数据交换空间2.2、可控的数据交换框架2.3、定…

HelloGitHub 社区动态,开启新的篇章!

今天这篇文章是 HelloGitHub 社区动态的第一篇文章&#xff0c;所以我想多说两句&#xff0c;聊聊为啥开启这个系列。 我是 2016 年创建的 HelloGitHub&#xff0c;它从最初的一份分享开源项目的月刊&#xff0c;现如今已经成长为 7w Star 的开源项目、1w 用户的开源社区、全网…

MATLAB中Stem3函数用法

目录 语法 说明 向量和矩阵数据 表数据 其他选项 示例 行向量输入 列向量输入 矩阵输入 使用向量输入指定针状线条位置 使用矩阵输入指定针状线条位置 填充标记 线型、标记符号和颜色选项 线型、标记符号和颜色选项 其他样式选项 绘制表中的数据 特定坐标区上…

读程序员的制胜技笔记08_死磕优化(上)

1. 过早的优化是万恶之源 1.1. 著名的计算机科学家高德纳(Donald Knuth)的一句名言 1.2. 原话是&#xff1a;“对于约97%的微小优化点&#xff0c;我们应该忽略它们&#xff1a;过早的优化是万恶之源。而对于剩下的关键的3%&#xff0c;我们则不能放弃优化的机会。” 2. 过早…

【前端】Jquery UI +PHP 实现表格拖动排序

目的&#xff1a;使用jquery ui库实现对表格拖拽排序&#xff0c;并且把排序保存到数据库中 效果如下 一、准备工作&#xff1a; 1、下载jquery ui库&#xff0c;可以直接引用线上路径 <link rel"stylesheet" href"https://code.jquery.com/ui/1.12.1/them…

C++中的函数重载:多功能而强大的特性

引言 函数重载是C编程语言中的一项强大特性&#xff0c;它允许在同一个作用域内定义多个同名函数&#xff0c;但这些函数在参数类型、个数或顺序上有所不同。本文将深入探讨函数重载的用法&#xff0c;以及它的优势和应用场景。 正文 在C中&#xff0c;函数重载是一项非常有…

【C#】Mapster对象映射的使用

系列文章 【C#】编号生成器&#xff08;定义单号规则、固定字符、流水号、业务单号&#xff09; 本文链接&#xff1a;https://blog.csdn.net/youcheng_ge/article/details/129129787 【C#】日期范围生成器&#xff08;开始日期、结束日期&#xff09; 本文链接&#xff1a;h…

【Java】SPI在Java中的实现与应用

一、SPI的概念 1.1、什么是API&#xff1f; API在我们日常开发工作中是比较直观可以看到的&#xff0c;比如在 Spring 项目中&#xff0c;我们通常习惯在写 service 层代码前&#xff0c;添加一个接口层&#xff0c;对于 service 的调用一般也都是基于接口操作&#xff0c;通…

【Git】如何安装git,项目中使用git上传到远程仓库,使用git中对多人使用出现的版本问题的解决

前言&#xff1a; 一&#xff0c;Git的介绍&#xff0c;安装&#xff0c;与SVN的对比 1.1Git的介绍 Git 是一个开源的分布式版本控制系统&#xff0c;用于敏捷高效地处理任何或小或大的项目。 Git 是 Linus Torvalds 为了帮助管理 Linux 内核开发而开发的一个开放源码的版本控…

民生画派创始人张龙(天驰)作品

简介 张龙&#xff08;天驰&#xff09; 中国民生画派创始人 首届“陆俨少奖”金奖得主 人民大学巨幅主题创作高级研修班导师 中央美院客座教授 神舟十二号载人飞船遨游太空搭载作品创作者 被评为2021、2022年年度最具收藏价值艺术家 中国美术家协会会员 中国美术家协…

【数据结构】单链表OJ题(二)

&#x1f525;博客主页&#xff1a; 小羊失眠啦. &#x1f3a5;系列专栏&#xff1a;《C语言》 《数据结构》 《Linux》《Cpolar》 ❤️感谢大家点赞&#x1f44d;收藏⭐评论✍️ 文章目录 一、分割链表二、回文链表三、相交链表四、环形链表 I五、环形链表 II 六、链表的深度拷…

17 Linux 中断

一、Linux 中断简介 1. Linux 中断 API 函数 ① 中断号 每个中断都有一个中断号&#xff0c;通过中断号可以区分出不同的中断。在 Linux 内核中使用一个 int 变量表示中断号。 ② request_irq 函数 在 Linux 中想要使用某个中断是需要申请的&#xff0c;request_irq 函数就是…

【python海洋专题四十四】海洋指数画法--多色渐变柱状图

【python海洋专题四十四】海洋指数画法–多色渐变柱状图

winform开发小技巧

如果我们不知道怎么在代码中new 一个控件&#xff0c;我们可以先在窗体中拉一个然后看Form1.Designer.cs 里面生成的代码就是我们要的 我们会在下面看到 还有泛型的使用&#xff0c;马上更新

Termius for Mac:掌控您的云端世界,安全高效的SSH客户端

你是否曾经在Mac上苦苦寻找一个好用的SSH客户端&#xff0c;让你能够远程连接到Linux服务器&#xff0c;轻松管理你的云端世界&#xff1f;现在&#xff0c;我们向你介绍一款强大而高效的SSH客户端——Termius。 Termius是一款专为Mac用户设计的SSH客户端&#xff0c;它提供了…

JavaScript从入门到精通系列第三十二篇:详解正则表达式语法(一)

文章目录 一&#xff1a;正则表达式 1&#xff1a;量词设置次数 2&#xff1a;检查字符串以什么开头 3&#xff1a;检查字符串以什么结尾 4&#xff1a; 同时使用开头结尾 5&#xff1a;同值开头同值结尾 二&#xff1a;练习 1&#xff1a;检查是否是一个手机号 大神链…