开发小技巧系列 - Java实现树形结构的方式有那些?

简介: 用java代码如何转一个列表的数据,转换成一个树形结构的数据,记录一下,可以帮助一般的开发者

开发小技巧系列文章,是本人对过往平台系统的设计开发及踩坑的记录与总结,给初入平台系统开发的开发人员提供参考与帮助。

在开发的过程中,有时候需要将集合的数据转换成一个树形结构,比如功能菜单、组织机构、或者商品分类等的场景。一般这些数据在数据表的存储中,都是采用行的方式来存储数据,方便对数据进行管理,在表的字段上会有“父ID(parent_id)”的字段,来表示关系。

假设有如下表结构及数据:

image.png

需要转换成一个树形结构,预期结构:

image.png

如何用java程序来实现呢?可能很多人会想到用递归来实现,这也是最常见的方式,可以实现一棵无限级的树形结果。是否还有其他实现方式呢?让我们先从递归实现来说起。
首先,需要定义一个树形结构的对象,如下:TreeVo.java

package net.jhelp.demo.tree;
import lombok.Data;
import java.util.List;
/** 
* 
* 
* @author : kame 
* @date: 2022/4/9 10:30 AM 
*/
@Data
public class TreeVo {
   
   
    private Integer id;
    private String name;
    private List<TreeVo> nodeList;
    public TreeVo(Integer id, String name) {
   
   
        this.id = id;
        this.name = name; 
   }
}

树形转换工具类 TreeKit.java , 递归函数的实现:

/**
     * list集合的行数据,转换成 tree 结构
     * @param beans
     * @return
     */    
public static List<TreeVo> list2Tree(List<Table> beans) {
   
   
        List<TreeVo> result = new ArrayList<>();
        if(!CollectionUtils.isEmpty(beans)){
   
   
            for(Table p : beans){
   
   
                if(p.getParentId() == null || p.getParentId() == 0) {
   
   
                    TreeVo vo = new TreeVo(p.getId(),p.getName());
                    vo.setNodeList(recurrence(beans, p));
                    result.add(vo);
                }
            } 
       }
       return result;
    }    /**
     *  递归函数
     * @param list
     * @param vo
     */
    private static List<TreeVo> recurrence(List<Table> list, Table vo){
   
   
        List<TreeVo> subNodes = new ArrayList<>();
        for(Table d : list){
   
   
            if (vo.getId().equals(d.getParentId())) {
   
   
                TreeVo sub = new TreeVo(d.getId(), d.getName());
                sub.setNodeList(recurrence(list, d));
                subNodes.add(sub);
            }
        } 
       return subNodes;
    }

测试用例:

    //模拟数据库查询出来的结果
    public static
            List<Table> tableData = Arrays.asList(
            new Table(1, 0, "根节点"),
            new Table(2, 1, "子节点1"),
            new Table(3, 2, "子节点1.1"), 
           new Table(4, 2, "子节点1.2"),
            new Table(5, 2, "子节点1.3"),
            new Table(6, 1, "子节点2"),
            new Table(7, 6, "子节点2.1"),
            new Table(8, 6, "子节点2.2"),
            new Table(9, 1, "子节点3"), 
           new Table(10, 9, "子节点3.1")
    );   

    @Test
    public void Bo2TreeTest(){
   
   
        List<TreeVo> result = TreeKit.list2Tree(tableData);
        log.info("递归:转换结果:{}", JsonUtil.toJson(result));
    }

执行结果为:

css复制代码[    {
   
           "id":1,        "name":"根节点",        "nodeList":[            {
   
                   "id":2,                "name":"子节点1",                "nodeList":[                    {
   
                           "id":3,                        "name":"子节点1.1",                        "nodeList":[                        ]
                    },
                    {
   
    
                       "id":4,
                        "name":"子节点1.2",
                        "nodeList":[
                        ]
                    },
                    {
   
   
                        "id":5,
                        "name":"子节点1.3",
                        "nodeList":[
                        ]
                    }
                ]
            },
            {
   
   
                "id":6,
                "name":"子节点2",
                "nodeList":[
                    {
   
   
                        "id":7,
                        "name":"子节点2.1",
                        "nodeList":[                        ]
                    },
                    {
   
   
                        "id":8,
                        "name":"子节点2.2",
                        "nodeList":[]
                    }
                ]
            },
            {
   
   
                "id":9,
                "name":"子节点3", 
                "nodeList":[
                    {
   
   
                        "id":10,
                        "name":"子节点3.1",
                        "nodeList":[                        ]
                    }
                ]
            }
        ]
    }
]

上面的程序都是使用For循环来实现List集合转换到Tree结构,大家是否还记得JDK8的一个新特性 -- Lambda表达式, 可能使用Stream来简化处理,那么程序会是怎么样呢?

用Stream流式的代码:

public static List<TreeVo> toTree(List<Table> datas){
   
   
        //得到父节点
        List<TreeVo> roots = datas.stream()
                .filter(m -> m.getParentId() == null || m.getParentId() == 0)
                .map(m -> {
   
   
                    TreeVo vo = new TreeVo(m.getId(), m.getName());
                    vo.setNodeList(buildSubNodes(m, datas));
                    return vo;
                })
                .collect(Collectors.toList());
        return roots;
    }    private static List<TreeVo> buildSubNodes(Table root, List<Table> list){
   
   
        List<TreeVo> subNodes = list.stream()
                .filter(m -> Objects.equals(root.getId(), m.getParentId()))
                .map(m -> {
   
   
            TreeVo vo = new TreeVo(m.getId(), m.getName());
            vo.setNodeList(buildSubNodes(m, list));
            return vo;
        }).collect(Collectors.toList());
        return subNodes;
    }

测试用例:

    @Test
    public void Bo2TreeTest2(){
   
   
        List<TreeVo> result = TreeKit.toTree(tableData);
        log.info("Stream流:转换结果:{}", JsonUtil.toJson(result));
    }

输出结果:

[    {
   
           "id":1,        "name":"根节点",        "nodeList":[            {
   
                   "id":2,                "name":"子节点1",                "nodeList":[                    {
   
                           "id":3,                        "name":"子节点1.1",                        "nodeList":[                        ]
                    },
                    {
   
    
                       "id":4,
                        "name":"子节点1.2",
                        "nodeList":[
                        ]
                    },
                    {
   
   
                        "id":5,
                        "name":"子节点1.3",
                        "nodeList":[
                        ]
                    }
                ]
            },
            {
   
   
                "id":6,
                "name":"子节点2",
                "nodeList":[
                    {
   
   
                        "id":7,
                        "name":"子节点2.1",
                        "nodeList":[                        ]
                    },
                    {
   
   
                        "id":8,
                        "name":"子节点2.2",
                        "nodeList":[]
                    }
                ]
            },
            {
   
   
                "id":9,
                "name":"子节点3", 
                "nodeList":[
                    {
   
   
                        "id":10,
                        "name":"子节点3.1",
                        "nodeList":[                        ]
                    }
                ]
            }
        ]
    }
]

从输出的结果来看,流式的处理方式,一样也可以实现将List集合转化成Tree的结果。如果读者还有更好的方式,欢迎留言讨论。

如果需要测试用例的代码,可以访问:gitee.com/TianXiaoSe_…

目录
相关文章
|
4天前
|
存储 Java 数据库连接
使用Java开发桌面应用程序
使用Java开发桌面应用程序
14 0
|
4天前
|
Java API 开发工具
java与Android开发入门指南
java与Android开发入门指南
11 0
|
4天前
|
分布式计算 负载均衡 Java
构建高可用性Java应用:介绍分布式系统设计与开发
构建高可用性Java应用:介绍分布式系统设计与开发
8 0
|
4天前
|
前端开发 安全 Java
使用Spring框架加速Java开发
使用Spring框架加速Java开发
10 0
|
4天前
|
前端开发 JavaScript Java
Java与Web开发的结合:JSP与Servlet
Java与Web开发的结合:JSP与Servlet
8 0
|
4天前
|
设计模式 算法 Java
设计模式在Java开发中的应用
设计模式在Java开发中的应用
15 0
|
4天前
|
监控 Java Maven
揭秘Java Agent技术:解锁Java工具开发的新境界
作为JDK提供的关键机制,Java Agent技术不仅为Java工具的开发者提供了一个强大的框架,还为性能监控、故障诊断和动态代码修改等领域带来了革命性的变革。本文旨在全面解析Java Agent技术的应用场景以及实现方式,特别是静态加载模式和动态加载模式这两种关键模式。
25 0
|
5天前
|
存储 Java 开发者
探索Java开发中触发空指针异常的场景
作为一名后端开发者在Java编程的世界中,想必大家对空指针并不陌生,空指针异常是一种常见而又令人头疼的问题,它可能会在我们最不经意的时候突然出现,给我们的代码带来困扰,甚至导致系统的不稳定性,而且最可怕的是有时候不能及时定位到它的具体位置。针对这个问题,我们需要深入了解触发空指针异常的代码场景,并寻找有效的方法来识别和处理这些异常情况,而且我觉得空指针异常是每个Java开发者都可能面临的挑战,但只要我们深入了解它的触发场景,并采取适当的预防和处理措施,我们就能够更好地应对这个问题。那么本文就来分享一下实际开发中一些常见的触发空指针异常的代码场景,并分享如何有效地识别和处理这些异常情况。
19 1
探索Java开发中触发空指针异常的场景
|
5天前
|
传感器 人工智能 前端开发
JAVA语言VUE2+Spring boot+MySQL开发的智慧校园系统源码(电子班牌可人脸识别)Saas 模式
智慧校园电子班牌,坐落于班级的门口,适合于各类型学校的场景应用,班级学校日常内容更新可由班级自行管理,也可由学校统一管理。让我们一起看看,电子班牌有哪些功能呢?
47 4
JAVA语言VUE2+Spring boot+MySQL开发的智慧校园系统源码(电子班牌可人脸识别)Saas 模式
|
13天前
|
Java 索引
Java String应用与开发
Java String应用与开发
22 0