构建Java树结构的三种实现方法

 更新时间:2025年06月24日 08:53:36   作者:yololee_  
这篇文章主要介绍了构建Java树结构的三种实现方法,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教

一、准备工作

表结构

CREATE TABLE `asset_classification` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键id',
  `className` varchar(50) NOT NULL COMMENT '分类名',
  `status` tinyint(2) NOT NULL COMMENT '状态(0:使用中 1:停用)',
  `type` tinyint(2) NOT NULL COMMENT '类别(0:设备 1:物料 2:附件)',
  `writeable` tinyint(2) NOT NULL COMMENT '1:不可编辑 0:可编辑',
  `parentId` int(11) NOT NULL COMMENT '父类id',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=157 DEFAULT CHARSET=utf8mb4 ROW_FORMAT=COMPACT;

部分表数据

在这里插入图片描述

二、三种方式

  • 原始递归
  • 利用Java 8 Stream流进行处理(原理还是递归)
  • Stream流升级构建

controller层

@RestController
public class TestController {

    @Autowired
    private AssetClassificationBiz assetClassificationBiz;

    @GetMapping("/tree")
    public HttpResponseTemp<?> getTree(){

        List<AssetClassification> assetClassificationList = assetClassificationBiz.selectAll();

        List<TreeUtil.TreeSelect> list = new ArrayList<>();

        for (AssetClassification assetClassification : assetClassificationList) {
            TreeUtil.TreeSelect treeSelect = new TreeUtil.TreeSelect();
            treeSelect.setId(Convert.toLong(assetClassification.getId()));
            treeSelect.setLabel(assetClassification.getClassName());
            treeSelect.setParentId(Convert.toLong(assetClassification.getParentId()));
            list.add(treeSelect);
        }

        //原始递归
        List<TreeUtil.TreeSelect> list1 = TreeUtil.buildTree(list);
        //利用Java 8 Stream流进行处理(原理还是递归)
        List<TreeUtil.TreeSelect> list2 = TreeUtil.buildTreeByStream(list);
        //Stream流升级构建
        List<TreeUtil.TreeSelect> list3 = TreeUtil.buildTreeByAllStream(list);

        return ResultStat.OK.wrap(list3);
    }
}

构建树结构工具类(三种方式)

/**
 * @ClassName TreeUtil
 * @Description 构建树结构工具类
 * @Author hl
 * @Date 2022/9/28 15:13
 * @Version 1.0
 */
public class TreeUtil {


    /**
     * Stream流升级构建
     * @param trees
     * @return
     */
    public static List<TreeSelect> buildTreeByAllStream(List<TreeSelect> trees){
        return trees.stream().filter(m -> m.getParentId() == 0).peek(
                (m) -> m.setChildren(getChildrenList(m, trees))
        ).collect(Collectors.toList());

    }

    /**
     * 获取子节点列表
     * @param tree
     * @param list
     * @return
     */
    public static List<TreeSelect> getChildrenList(TreeSelect tree, List<TreeSelect> list){
        return list.stream().filter(item -> Objects.equals(item.getParentId(), tree.getId())).peek(
                (item) -> item.setChildren(getChildrenList(item, list))
        ).collect(Collectors.toList());
    }



    /**
     * 利用Java 8 Stream流进行处理(原理还是递归)
     * @param trees
     * @return
     */
    public static List<TreeSelect> buildTreeByStream(List<TreeSelect> trees){
        //获取parentId = 0的根节点
        List<TreeSelect> list = trees.stream().filter(item -> item.getParentId() == 0L).collect(Collectors.toList());
        //根据parentId进行分组
        Map<Long, List<TreeSelect>> map = trees.stream().collect(Collectors.groupingBy(TreeSelect::getParentId));
        recursionFnTree(list, map);
        return list;
    }

    /**
     * 递归遍历节点
     * @param list
     * @param map
     */
    public static void recursionFnTree(List<TreeSelect> list, Map<Long, List<TreeSelect>> map){
        for (TreeSelect treeSelect : list) {
            List<TreeSelect> childList = map.get(treeSelect.getId());
            treeSelect.setChildren(childList);
            if (null != childList && 0 < childList.size()){
                recursionFnTree(childList,map);
            }
        }
    }


    /**
     * 构建前端所需要树结构
     *
     * @param trees 列表
     * @return 树结构列表
     */
    public static List<TreeSelect> buildTree(List<TreeSelect> trees) {
        List<TreeSelect> returnList = new ArrayList<>();
        List<Long> tempList = new ArrayList<>();
        for (TreeSelect tree : trees) {
            tempList.add(tree.getId());
        }

        for (TreeSelect treeSelect : trees) {
            // 如果是顶级节点, 遍历该父节点的所有子节点
            if (!tempList.contains(treeSelect.getParentId())) {
                recursionFn(trees, treeSelect);
                returnList.add(treeSelect);
            }
        }

        if (returnList.isEmpty()) {
            returnList = trees;
        }
        return returnList;
    }
    /**
     * 递归列表
     */
    private static void recursionFn(List<TreeSelect> list, TreeSelect t) {
        // 得到子节点列表
        List<TreeSelect> childList = getChildList(list, t);
        t.setChildren(childList);
        for (TreeSelect tChild : childList) {
            if (hasChild(list, tChild)) {
                recursionFn(list, tChild);
            }
        }
    }

    /**
     * 得到子节点列表
     */
    private static List<TreeSelect> getChildList(List<TreeSelect> list, TreeSelect t) {
        List<TreeSelect> tlist = new ArrayList<>();
        for (TreeSelect n : list) {
            if (ObjectUtil.isNotNull(n.getParentId()) && n.getParentId().longValue() == t.getId().longValue()) {
                tlist.add(n);
            }
        }
        return tlist;
    }

    /**
     * 判断是否有子节点
     */
    private static boolean hasChild(List<TreeSelect> list, TreeSelect t) {
        return getChildList(list, t).size() > 0;
    }



    static class TreeSelect implements Serializable {

        /** 节点ID */
        private Long id;

        /** 节点名称 */
        private String label;

        /** 父ID */
        private Long parentId;

        /** 子节点 */
        private List<TreeSelect> children;

        public Long getId() {
            return id;
        }

        public void setId(Long id) {
            this.id = id;
        }

        public String getLabel() {
            return label;
        }

        public void setLabel(String label) {
            this.label = label;
        }

        public List<TreeSelect> getChildren() {
            return children;
        }

        public void setChildren(List<TreeSelect> children) {
            this.children = children;
        }

        public Long getParentId() {
            return parentId;
        }

        public void setParentId(Long parentId) {
            this.parentId = parentId;
        }
    }
}

执行结果

在这里插入图片描述

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • Java实现四连环棋游戏

    Java实现四连环棋游戏

    这篇文章主要为大家详细介绍了Java实现四连环棋游戏,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2020-10-10
  • java数据库数据分批读取的实现示例

    java数据库数据分批读取的实现示例

    在处理大量数据时,直接从数据库一次性读取所有数据可能会导致内存溢出或者性能下降,本文就来介绍一下java数据库数据分批读取的实现示例,感兴趣的可以了解一下
    2024-01-01
  • springmvc实现导出数据信息为excle表格示例代码

    springmvc实现导出数据信息为excle表格示例代码

    本篇文章主要介绍了springmvc实现导出数据信息为excle表格,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧。
    2017-01-01
  • SpringMVC整合SSM实现表现层数据封装详解

    SpringMVC整合SSM实现表现层数据封装详解

    这篇文章主要介绍了SpringMVC整合SSM实现表现层数据封装,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习吧
    2022-10-10
  • Java springboot探究配置文件优先级

    Java springboot探究配置文件优先级

    在springboot项目中,我们可以通过在yml文件中设置变量,再通过@Value注解来获得这个变量并使用,但如果这个项目已经部署到服务器上,我们想更改这个数据了需要怎么做呢,其实在springboot项目中,配置文件是有优先级的
    2023-04-04
  • Java中的Timer和TimerTask详细解读

    Java中的Timer和TimerTask详细解读

    这篇文章主要介绍了Java中的Timer和TimerTask详细解读,  Timer和TimerTask可以做为实现线程的第三种方式,前两中方式分别是继承自Thread类和实现Runnable接口,需要的朋友可以参考下
    2023-10-10
  • MybatisPlus处理大表查询的实现步骤

    MybatisPlus处理大表查询的实现步骤

    在实际工作中当指定查询数据过大时,我们一般使用分页查询的方式一页一页的将数据放到内存处理,本文主要介绍了MybatisPlus处理大表查询的实现步骤,感兴趣的可以了解一下
    2024-08-08
  • 解析Mybatis SqlSessionFactory初始化原理

    解析Mybatis SqlSessionFactory初始化原理

    本文主要介绍了Mybatis SqlSessionFactory初始化原理,文中通过示例代码介绍的非常详细,需要的朋友们下面随着小编来一起学习学习吧
    2021-07-07
  • Java中Json与List、Map、entity的互相转化

    Java中Json与List、Map、entity的互相转化

    在开发中,Json转换的场景往往也就是那么几个,本文主要介绍了Java中Json与List、Map、entity的互相转化,具有一定的参考价值,感兴趣的可以了解一下
    2022-07-07
  • Java中的四种单例模式浅析

    Java中的四种单例模式浅析

    这篇文章主要给大家介绍了关于Java中四种单例模式的相关资料,其中包括饿汉式、懒汉式、懒汉式(双重锁)及内部类等四种,分别给出了详细的示例代码和介绍,需要的朋友们下面来一起看看吧。
    2017-05-05

最新评论