Java递归实现树形结构的两种方式

news2024/11/20 21:28:56

目录

      • 0、引言
      • 1、数据准备
      • 2、类型转化
      • 3、递归实现方法
        • 3.1、Java7及以下纯Java递归实现
        • 3.2、Java8及以上借助lamda表达式实现

0、引言

在开发的过程中,很多业务场景需要一个树形结构的结果集进行前端展示,也可以理解为是一个无限父子结构,常见的有报表指标结构、菜单结构等。Java中递归实现树形结构的两种常见方式如下:

  • Java7及以下纯Java递归实现
  • Java8及以上借助lamda表达式实现

1、数据准备

Java实体类NodePO对应数据库表

package com.wbs.pojo;

import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.List;

@Data
@NoArgsConstructor
public class NodePO {

    /**
     * 当前节点id
     */
    private String id;

    /**
     * 当前节点名称
     */
    private String name;

    /**
     * 父级节点id
     */
    private String parentId;

    /**
     * 当前节点序号
     */
    private String orderNo;

    /**
     * 子集节点
     */
    private List<NodePO> children;

    /**
     * 构造函数
     * @param id
     * @param name
     * @param parentId
     * @param orderNo
     */
    public NodePO(String id,String name,String parentId,String orderNo){
        this.id = id;
        this.name = name;
        this.parentId = parentId;
        this.orderNo = orderNo;
    }
}

​ 自己造一些数据模拟从数据库中查询出来的数据:

static final List<NodePO> nodePOs = Arrays.asList(
            new NodePO("1","一级节点1",null,"_0001"),
            new NodePO("2","二级节点1.1","1","_0002"),
            new NodePO("3","二级节点1.2","1","_0003"),

            new NodePO("4","一级节点2",null,"_0004"),
            new NodePO("5","二级节点2.1","4","_0005"),
            new NodePO("6","二级节点2.2","4","_0006"),
            new NodePO("7","三级节点2.2.1","6","_0007"),

            new NodePO("8","一级节点3",null,"_0008"),
            new NodePO("9","二级节点3.1","8","_0009"),
            new NodePO("10","三级节点3.1.1","9","_0010"),
            new NodePO("11","四级节点3.1.1.1","10","_0011"),
            new NodePO("12","五级节点3.1.1.1.1","11","_0012")
    );

2、类型转化

从开发的过程中发现直接操作实体类集合,专门指定某一个实体类封装的方法是不具有普适性的,所以将实体类集合统一转化为Map集合,操作方便,具有一定的普适性:

List<Map<String, Object>> mapList = BeanMapUtils.listBeanToListMap(jsonObject);

BeanMapUtils自己简单封装一个工具类(不惧普适性勿喷):

package com.wbs.util;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import lombok.SneakyThrows;
import org.springframework.cglib.beans.BeanMap;

import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @author 一宿君
 * @version Id: BeanMapUtils.java, v 0.1 Administrator Exp $$
 * @date 2022-10-13 14:24:20
 * @desc java实体类和map相互转换工具类
 */
public class BeanMapUtils {

    /**
     * 将实体类对象属性转化为map对象
     * @param t
     * @param <T>
     * @return
     */
    public static <T> Map<String, Object> beanToMap(T t) {
        Map<String, Object> map = new HashMap<>();
        if (t != null) {
            if (t instanceof JSONObject){
                return (JSONObject)t;
            }
            BeanMap beanMap = BeanMap.create(t);
            for (Object key : beanMap.keySet()) {
                map.put(key.toString(), beanMap.get(key));
            }
        }
        return map;
    }


    /**
     * 将map对象中转化为实体类对象
     * @param map
     * @param clazz
     * @param <T>
     * @return
     * @throws Exception
     */
    public static <T> T mapToBean(Map<String, Object> map,Class<T> clazz) throws Exception {
        T bean = clazz.newInstance();
        if (bean instanceof JSONObject){
            JSONObject jsonObject = (JSONObject)bean;
            Set<Map.Entry<String, Object>> entries = map.entrySet();
            for (Map.Entry<String, Object> entry : entries) {
                jsonObject.put(entry.getKey(),entry.getValue());
            }
            return (T)jsonObject;
        }
        BeanMap beanMap = BeanMap.create(bean);
        beanMap.putAll(map);
        return bean;
    }

    /**
     * 通过lambda表达式将List<JavaBean>转化为List<Map<String, Object>>
     * @param objList
     * @param <T>
     * @return
     */
    public static <T> List<Map<String, Object>> listBeanToListMap(List<T> objList) {
        return objList.stream().map(new Function<T, Map<String, Object>>() {
            @Override
            public Map<String, Object> apply(T t) {
                Map<String,Object> map = Maps.newHashMap();
                if (t instanceof JSONObject){
                    return (JSONObject)t;
                }
                BeanMap beanMap = BeanMap.create(t);
                for (Object key : beanMap.keySet()) {
                    map.put(key.toString(), beanMap.get(key));
                }
                return map;
            }
        }).collect(Collectors.toList());
    }

    /**
     * 通过lambda表达式将List<Map<String, Object>>转化为List<JavaBean>
     * @param mapList
     * @param <T>
     * @return
     */
    public static <T> List<T> listMapToListBean(List<Map<String,Object>> mapList,Class<T> clazz) {
        return mapList.stream().map(new Function<Map<String, Object>,T>() {
            @SneakyThrows
            @Override
            public T apply(Map<String, Object> map) {
                T t = clazz.newInstance();
                if (t instanceof JSONObject){
                    return (T)map;
                }
                BeanMap beanMap = BeanMap.create(t);
                beanMap.putAll(map);
                return t;
            }
        }).collect(Collectors.toList());
    }
}

其中org.springframework.cglib.beans.BeanMap;org.springframework:spring-core依赖下的工具包,spring-core核心依赖只要导入spring-boot-starter依赖即可

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter</artifactId>
    <version>2.2.0.RELEASE</version>
</dependency>

在这里插入图片描述

3、递归实现方法

3.1、Java7及以下纯Java递归实现

既然是Java7及以下实现方式,那排序也用最原始的冒泡排序:

/**
     * 冒泡排序,小的在前,大的在后
     * @param list
     * @return
     */
    public static List<Map<String, Object>> sortJava7Map(List<Map<String, Object>> list){
        if(CollectionUtils.isEmpty(list)){
            return Lists.newArrayList();
        }
        boolean flag;
        int size = list.size();
        for (int i = 0; i < size - 1; i++) {
            flag = false;
            for (int j = 1; j < size - i; j++) {
                Map<String, Object> frontMap = list.get(j - 1);
                Map<String, Object> afterMap = list.get(j);
                if (String.valueOf(frontMap.get("orderNo")).compareTo(String.valueOf(afterMap.get("orderNo"))) > 0){
                    list.set(j - 1,afterMap);
                    list.set(j,frontMap);
                    flag = true;
                }
            }
            //如果没有发生位置互换,则退出循环
            if (!flag){
                break;
            }
        }
        return list;
    }

给定一个节点,获取它的所有子节点:

/**
     * Java7及以下版本获取子节点的方式
     * @param parentNode
     * @param allList
     * @return
     */
    public static List<Map<String, Object>> getJava7Children(Map<String,Object> parentNode,List<Map<String, Object>> allList){

        //存放当前节点的直系子节点
        List<Map<String, Object>> curNodeChildrenList = Lists.newArrayList();

        //存放直系子节点以外的节点
        List<Map<String, Object>> otherNodeList = Lists.newArrayList();

        Object pId = parentNode.get("id");
        for (Map<String, Object> map : allList) {
            Object curPId = map.get("parentId");
            if (ObjectUtils.isNotEmpty(curPId) && Objects.equals(pId,curPId)){
                curNodeChildrenList.add(map);
            }else {
                otherNodeList.add(map);
            }
        }
        if (curNodeChildrenList.isEmpty()){
            return curNodeChildrenList;
        }
        //每一层级都进行排序
        curNodeChildrenList = sortJava7Map(curNodeChildrenList);

        //迭代直系子节点再获取子节点
        for (Map<String, Object> map : curNodeChildrenList) {
            map.put("children",getJava7Children(map,otherNodeList));
        }
        return curNodeChildrenList;
    }

给出一个结果集,构建树形结果集:

/**
     * 使用Java7的方式获取树形结构
     * @param allList
     * @return
     */
    public static List<Map<String, Object>> getJava7ResultTree(List<Map<String, Object>> allList){
        //存放所有的一级节点
        List<Map<String, Object>> oneLevelNodeList = Lists.newArrayList();

        for (Map<String, Object> map : allList) {
            if (ObjectUtils.isEmpty(map.get("parentId"))){
                map.put("children",getJava7Children(map,allList));
                oneLevelNodeList.add(map);
            }
        }
        return sortJava8Map(oneLevelNodeList);
    }

获取树形结构:

//转化为Map集合
List<Map<String, Object>> mapList = BeanMapUtils.listBeanToListMap(nodePOs);
//获取树形结构
List<Map<String, Object>> java7ResultTree = getJava7ResultTree(mapList);
//打印输出
System.out.println(JSON.toJSONString(java7ResultTree));

打印结果:

[{"orderNo":"_0001","children":[{"orderNo":"_0002","children":[],"name":"二级节点1.1","id":"2","parentId":"1"},{"orderNo":"_0003","children":[],"name":"二级节点1.2","id":"3","parentId":"1"}],"name":"一级节点1","id":"1"},{"orderNo":"_0004","children":[{"orderNo":"_0005","children":[],"name":"二级节点2.1","id":"5","parentId":"4"},{"orderNo":"_0006","children":[{"orderNo":"_0007","children":[],"name":"三级节点2.2.1","id":"7","parentId":"6"}],"name":"二级节点2.2","id":"6","parentId":"4"}],"name":"一级节点2","id":"4"},{"orderNo":"_0008","children":[{"orderNo":"_0009","children":[{"orderNo":"_0010","children":[{"orderNo":"_0011","children":[{"orderNo":"_0012","children":[],"name":"五级节点3.1.1.1.1","id":"12","parentId":"11"}],"name":"四级节点3.1.1.1","id":"11","parentId":"10"}],"name":"三级节点3.1.1","id":"10","parentId":"9"}],"name":"二级节点3.1","id":"9","parentId":"8"}],"name":"一级节点3","id":"8"}]

树形结构搞定!

3.2、Java8及以上借助lamda表达式实现

Java7的方式虽然实现了树形结构,但是有一定的缺点,比如:代码量比较大,逻辑相对较复杂,那Java8是如何简化,如下所示:

既然Java8有lamda表达式,那代码我们能省就省,先看排序,一行代码搞定:

/**
     * 根据orderNo排序树形结构的每一个层级
     * @param list
     * @return
     */
    public static List<Map<String, Object>> sortJava8Map(List<Map<String, Object>> list){
        if(CollectionUtils.isEmpty(list)){
            return Lists.newArrayList();
        }
        //关键之处,一行代码搞定
        list.sort(Comparator.comparing(m -> String.valueOf(m.get("orderNo"))));
        return list;
    }

给定一个节点,获取它的所有子节点:

释义:
filter: 过滤,相当于for循环,再if条件判断。
peek: 给定一个节点,往它的children塞子节点。

/**
     * 根据父级节点获取所有的子集节点
     * @param parentNode
     * @param allList
     * @return
     */
    public static List<Map<String, Object>> getJava8Children(Map<String,Object> parentNode, List<Map<String, Object>> allList){
        return allList.stream()
                .filter(curNode -> ObjectUtils.isNotEmpty(curNode.get("parentId")) && Objects.equals(curNode.get("parentId"),parentNode.get("id")))
                .peek(m -> m.put("children", getJava8Children(m,allList))).collect(Collectors.toList());
    }

给出一个结果集,构建树形结果集:

/**
     * 获取树形结构
     * @param mapList
     * @return treeList 树形结果集
     */
    public static List<Map<String, Object>> getJava8ResultTree(List<Map<String, Object>> mapList){
        if (CollectionUtils.isEmpty(mapList)){
            return Lists.newArrayList();
        }
        //filter过滤出所有的一级节点
        return mapList.stream().filter(m -> Objects.equals(m.get("parentId"), null) || Objects.equals(m.get("parentId"), ""))
                .peek(m -> m.put("children", sortJava8Map(getJava8Children(m, mapList)))).collect(Collectors.toList());
    }

获取树形结构:

//转化为Map集合
List<Map<String, Object>> mapList = BeanMapUtils.listBeanToListMap(nodePOs);
//获取树形结构
List<Map<String, Object>> java8ResultTree = getJava8ResultTree(mapList);
//打印输出
System.out.println(JSON.toJSONString(java8ResultTree));

打印结果:

[{"orderNo":"_0001","children":[{"orderNo":"_0002","children":[],"name":"二级节点1.1","id":"2","parentId":"1"},{"orderNo":"_0003","children":[],"name":"二级节点1.2","id":"3","parentId":"1"}],"name":"一级节点1","id":"1"},{"orderNo":"_0004","children":[{"orderNo":"_0005","children":[],"name":"二级节点2.1","id":"5","parentId":"4"},{"orderNo":"_0006","children":[{"orderNo":"_0007","children":[],"name":"三级节点2.2.1","id":"7","parentId":"6"}],"name":"二级节点2.2","id":"6","parentId":"4"}],"name":"一级节点2","id":"4"},{"orderNo":"_0008","children":[{"orderNo":"_0009","children":[{"orderNo":"_0010","children":[{"orderNo":"_0011","children":[{"orderNo":"_0012","children":[],"name":"五级节点3.1.1.1.1","id":"12","parentId":"11"}],"name":"四级节点3.1.1.1","id":"11","parentId":"10"}],"name":"三级节点3.1.1","id":"10","parentId":"9"}],"name":"二级节点3.1","id":"9","parentId":"8"}],"name":"一级节点3","id":"8"}]

树形结构搞定!两种实现方式对比一下,你就说Java8的方式哇塞不哇塞!!!


在这里插入图片描述

一起学编程,让生活更随和!如果你觉得是个同道中人,欢迎关注博主公众号:【随和的皮蛋桑】。专注于Java基础、进阶、面试以及计算机基础知识分享🐳。偶尔认知思考、日常水文🐌。

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【老师见打系列】:我只是写了一个自动回复讨论的脚本~

文章目录&#x1f31f;好久不见⛳️实现过程&#x1f334;老操作了兄弟们~&#x1f422;一步拿捏讨论&#x1f496;美图结束语专栏Python零基础入门篇&#x1f4a5;Python网络蜘蛛&#x1f4a5;Python数据分析Django基础入门宝典&#x1f4a5;小玩意儿&#x1f4a5;Web前端学习…

2022海德堡桂冠论坛(HLF)见闻录

今年9月下旬&#xff0c;我前往德国参加了第九届海德堡桂冠论坛。因疫情原因停摆两年后&#xff0c;海德堡桂冠论坛再次以线下形式举办&#xff0c;会场热闹非凡&#xff0c;作为计算机与数学界的社交盛宴当之无愧。 海德堡桂冠论坛&#xff08;Heidelberg Laureate Forum, HLF…

IGV-GSAman |「功能基因组时代」的高效率科研工具

写在前面 今天周末&#xff0c;转眼10月份只剩一周。万万没想到&#xff0c;一个月下去&#xff0c;我还是花了不少时间在完善「GSAman」。至于为什么本来「两个小时」就干完的事情&#xff0c;可以干成「22天」&#xff1f;到底还是我对前面的版本&#xff0c;不太满意。当然…

Attack Lab

Attack Lab 从CMU官网下载完所需实验包后&#xff0c;内有官方文档以及.tar压缩包&#xff0c;使用tar -xvf targetk.tar解压后&#xff0c;得到如下文件 The fifiles in targetk include: README.txt: A fifile describing the contents of the directory ctarget: An execut…

web自动化测试框架

本文介绍web自动化测试框架 ●Base&#xff1a;用来对Selenium API进行二次封装。 对Selenium API进行二次封装的目的是简化一些复杂的操作&#xff0c;但是千万不要为了封装而封装。 封装好后&#xff0c;其他页面类可以集成basepage&#xff0c;调用这些方法。 from sele…

【iOS】—— 仿写知乎日报第一周总结

目录知乎日报第一周完成情况遇到的问题&#xff1a;1.线程问题&#xff1a;2.SDWebImage加载图片3.实现无限刷新界面4.点击主界面cell进入后的界面知乎日报第一周完成情况 在这周开始了仿写知乎日报的任务&#xff0c;在第一周里&#xff0c;我完成了主界面&#xff0c;以及滚…

新人入手mac折腾过程中遇到的解决方案

本文将长期更新&#xff0c;以记录个人的使用。 终端美化 …还没有美化完&#xff0c;不过推荐看这篇文章Mac终端美化指南 homebrew homebrew是Mac OS X上的强大的包管理工具&#xff0c;可以高效管理各种软件包&#xff0c;官方说法是&#xff1a;The missing package mana…

ArcGIS:如何新建图层组并添加数据、切换数据视图和布局视图、修改符号系统?

目录 01 如何新建图层组&#xff1f; 02 如何在图层组中添加数据 03 如何切换数据视图和布局视图 03 如何修改符号系统&#xff1f; 3.1 如何快捷的修改一下符号样式&#xff1f; 3.2 如何修改符号系统的色带 3.2.1 色带视图的取消 3.3 修改符号系统中的标注显示 3.4 如…

大气湍流退化图像复原技术研究及DSP实现

目录 第一章 绪论 1 1.1 研究背景 1 1.2 国内外研究现状 1 1.3 本文的研究内容 5 1.4 本文的组织结构 5 第二章 大气湍流退化图像复原技术 7 2.1 图像退化及复原数学模型概述 7 2.1.1 图像退化的数学模型 7 2.1.2 图像复原的数学模型 8 2.2 大气湍流退化图像概述 9 2.2.1 大气…

插件内存分析

rtspsrc 1. 当pipleline为rtspsrc加其他插件时&#xff0c;如果让其他插件卡主会发生什么事情呢&#xff1f;以rtspsrcfakesink为例&#xff08;gst-launch-1.0.exe rtspsrc locationrtsp://xxx ! fakesink&#xff09;&#xff0c;修改fakesink的代码&#xff0c;让render中…

基于SSM的图书馆阅览室预约管理系统,高质量论文范例,可直接参考使用,附送源码、数据库脚本

目录 1.项目技术栈 2.适合对象 3.适合课题 4.项目功能概述 4.1 项目功能汇总 4.2 项目功能介绍 5. 高质量论文范例 6. 毕业设计撰写视频教程 6.部分运行截图 1.项目技术栈 前端必学三个基础&#xff0c;"HTML、CSS、JS"&#xff0c;基本每个B/S架构项目都要…

C++【搜索二叉树】

目录 一、什么是搜索二叉树 二、搜索二叉树如何删除数据 删除的是叶子结点的情况 删除的结点下面仅有一个子节点&#xff08;托孤&#xff09;&#xff08;要删除的结点只有一个孩子&#xff09; 替换法删除 &#xff08;要删除的结点有两个个孩子&#xff09; 三、写一…

腾讯前辈熬夜肝了一个月整理的《Linux内核学习笔记》,啃完受益匪浅不走弯路

小编热衷于收集整理资源&#xff0c;记录踩坑到爬坑的过程。希望能把自己所学&#xff0c;实际工作中使用的技术、学习方法、心得及踩过的一些坑&#xff0c;记录下来。也希望想做Linux内核高级工程师的你一样&#xff0c;通过我的分享可以少走一些弯路&#xff0c;可以形成一套…

【数据结构】常见七大排序总结

目录 一、插入排序&#xff1a;直接插入排序【稳定排序方法】 二、插入排序&#xff1a;希尔排序【不稳定排序方法】 三、选择排序&#xff1a;直接选择排序【不稳定排序方法】 四、选择排序&#xff1a;堆排序【不稳定排序方法】 五、交换排序&#xff1a;冒泡排序【稳定…

基于单片机MC9S12XS128的两轮自平衡小车设计

目 录 1.绪论 1 1.1研究背景与意义 1 1.2两轮自平衡车的关键技术 2 1.2.1系统设计 2 1.2.2数学建模 2 1.2.3姿态检测系统 2 1.2.4控制算法 3 1.3本文主要研究目标与内容 3 1.4论文章节安排 3 2.系统原理分析 5 2.1控制系统要求分析 5 2.2平衡控制原理分析 5 2.3自平衡小车数学…

总结一下flex布局

flex布局 传统布局方案是基于盒状模型&#xff0c;依赖 display position float 方式来实现&#xff0c;灵活性较差&#xff1b;Flex是Flexible Box的缩写&#xff0c;意为”弹性布局”。Flex可以简便、完整、响应式地实现多种页面布局 CSS3 弹性盒子是一种一维的布局&…

[Spring MVC3]MyBatis详解

本章重点讲述了MyBatis映射器&#xff0c;对数据层进行的操作&#xff0c;建议本篇文章和Spring Boot的持久层相互比较来看会更加收获颇多Spring Boot持久层技术 本文需要使用到MVC第一讲的模板格式与配置情况&#xff0c;全部代码已经放在此博客中&#xff0c;Spring MVC1 目…

修改设备管理器的COM端口名称

Windows Registry Editor Version 5.00 [HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Control\Class{4d36e978-e325-11ce-bfc1-08002be10318}\0001] “InfPath”“oem53.inf” “InfSection”“CH341SER_Inst.NTamd64” “ProviderName”“wch.cn” “DriverDateData”hex:00,00…

计算机算法(二)——进入计算机世界

作者介绍&#xff1a; &#x1f4de;作者&#xff1a;小刘在C站 &#x1f4c0;每天分享课堂笔记 &#x1f339;夕阳下&#xff0c;是最美的绽放 瑞士著名的科学家Niklaus Wirth教授曾提出&#xff1a;数据结构算法程序。 数据结构是程序的骨 算法是程序的灵魂。 在生活…

【 C++11 】lambda表达式

目录 1、lambda表达式的引入 2、lambda表达式 lambda表达式的语法 lambda表达式捕捉列表说明 使用lambda表达式排序自定义类型 lambda表达式的底层原理 1、lambda表达式的引入 在C98中&#xff0c;如果想要对一个数据集合中的元素进行排序&#xff0c;可以使用std::sort方法&…