【Java工具类】使用这3种原生方式可以轻松构建Java树形结构数据

前言

在开发的过程中很多业务场景需要一个树形结构的结果集进行前端展示,比如菜单结构、部门列表、文件结构等,也可以理解为是一个无限父子结构。

树形结构类如下:

@Data
public class Category {
    //唯一id
    private Long id;
    //名称
    private String name;
    //父id,为null表示定义父类
    private Long parentId;
    //所属子类
    private List<Category> children;
    //同一级分类排序
    private Integer sort;

    public Category(Long id, String name, Long parentId) {
        this.id = id;
        this.name = name;
        this.parentId = parentId;
    }
}

原始递归

public class Test1 {
 
    public static void main(String[] args) {
        // 获取顶级分类列表,并打印为JSON格式
        List<Category> categories = parentList01();
        System.out.println(JSON.toJSONString(categories, true));
    }
 
    // 获取顶级分类列表的方法
    private static List<Category> parentList01() {
        // 过滤出parentId为null的顶级分类
        List<Category> parentList = Category.categories.stream()
                .filter(category -> category.getParentId() == null)
                .collect(Collectors.toList());
        // 为每个顶级分类设置其子分类
        for (Category category : parentList) {
            category.setChildren(buildChildren(category.getId(), Category.categories));
        }
        return parentList;
    }
 
    // 递归构建子分类列表的方法
    private static List<Category> buildChildren(Long id, List<Category> categories) {
        List<Category> children = new ArrayList<>();
        for (Category category : categories) {
            // 跳过parentId不为null的非顶级分类(这里实际上是多余的,因为外层循环已经过滤了顶级分类)
            if (category.getParentId() != null) {
                continue;
            }
            // 找到parentId等于指定id的分类,加入到子分类列表中
            if (category.getParentId().equals(id)) {
                children.add(category);
            }
        }
        // 递归地为每个子分类设置其子分类
        for (Category child : children) {
            child.setChildren(buildChildren(child.getId(), categories));
        }
        return children;
    }
}

利用Java 8 Stream流进行处理(原理还是递归)

public class Test2 {

    /**
     * 获取带有树形结构的分类列表
     *
     * @return 分类的树形结构列表
     */
    public static List<Category> listWithTree() {
        //1、查询所有分类
        List<Category> entities = Category.categories; // 假设Category.categories包含了所有分类的静态列表

        //2、将这些分类组装成父子关系的树形结构
        //2.1)找到所有的一级分类,并为它们设置子分类
        return entities.stream()
                // 过滤出所有的一级分类(即父分类ID为null的分类)
                .filter(category -> category.getParentId() == null)
                // 处理每个一级分类,递归地为它们设置子菜单
                .peek(menu -> menu.setChildren(getChildless(menu, entities)))
                // 根据sort属性对分类进行排序
                .sorted(Comparator.comparingInt(c -> c.getSort() == null ? 0 : c.getSort()))
                .collect(Collectors.toList()); // 收集结果到列表中
    }

    /**
     * 递归查找给定根分类的所有子分类
     *
     * @param root 根分类
     * @param all  所有分类的列表
     * @return 根分类的所有子分类列表
     */
    private static List<Category> getChildless(Category root, List<Category> all) {
        return all.stream()
                // 过滤出所有父分类ID等于当前根分类ID的分类
                .filter(category -> root.getId().equals(category.getParentId()))
                .peek(category -> {
                    // 递归地为每个子分类设置其子分类
                    category.setChildren(getChildless(category, all));
                })
                // 根据sort属性对子分类进行排序
                .sorted(Comparator.comparingInt(c -> c.getSort() == null ? 0 : c.getSort()))
                .collect(Collectors.toList()); // 收集结果到列表中
    }

    /**
     * 主方法,用于测试listWithTree方法
     *
     * @param args 命令行参数
     */
    public static void main(String[] args) {
        // 将树形结构的分类列表转换为JSON字符串并打印出来
        System.out.println(JSON.toJSONString(listWithTree(), true));
    }
}

Stream流升级构建

public class Test3 {

    // 主方法,程序的入口点
    public static void main(String[] args) {
        // 将树形结构的分类列表转换为JSON字符串并打印出来
        // 假设JSON.toJSONString是一个将对象转换为JSON字符串的静态方法
        System.out.println(JSON.toJSONString(listWithTree(), true));
    }

    // 构建树形结构的分类列表
    public static List<Category> listWithTree() {
        // 从某个静态源(可能是数据库或配置)获取所有分类的列表
        List<Category> categories = Category.categories;

        // 过滤出所有一级分类(即父分类ID为null的分类),并为每个一级分类设置子分类
        List<Category> collect = categories
                .stream() // 将列表转换为流
                .filter(m -> m.getParentId() == null) // 过滤出一级分类
                .map((m) -> { // 对每个一级分类进行处理
                    m.setChildren(getChildrenList(m, categories)); // 设置子分类
                    return m; // 返回处理后的分类
                })
                .collect(Collectors.toList()); // 收集结果到新的列表中

        return collect; // 返回构建好的树形结构列表
    }
	/**
	 * 获取子节点列表
	 * @param tree 父节点
	 * @param list 分类列表
	 * @return 子节点列表
	 */
    public static ListCategory> getChildrenList(Category tree, List<Category> list) {
        // 从列表中过滤出所有父分类ID等于给定父分类ID的分类
        List<Category> children = list
                .stream()
                .filter(item -> Objects.equals(item.getParentId(), tree.getId()))
                .map((item) -> { // 对每个子分类进行处理
                    item.setChildren(getChildrenList(item, list)); // 递归设置子分类的子分类
                    return item; // 返回处理后的子分类
                })
                .collect(Collectors.toList()); // 收集结果到新的列表中

        return children; // 返回构建好的子分类列表
    }
}
$(function(){ $.fn.extend({ SimpleTree:function(options){ //初始化参数 var option = $.extend({ click:function(a){ } },options); option.tree=this; /* 在参数对象中添加对当前菜单树的引用,以便在对象中使用该菜单树 */ option._init=function(){ /* * 初始化菜单展开状态,以及分叉节点的样式 */ this.tree.find("ul ul").hide(); /* 隐藏所有子级菜单 */ this.tree.find("ul ul").prev("li").removeClass("open"); /* 移除所有子级菜单父节点的 open 样式 */ this.tree.find("ul ul[show=&#39;true&#39;]").show(); /* 显示 show 属性为 true 的子级菜单 */ this.tree.find("ul ul[show=&#39;true&#39;]").prev("li").addClass("open"); /* 添加 show 属性为 true 的子级菜单父节点的 open 样式 */ }/* option._init() End */ /* 设置所有超链接不响应单击事件 */ this.find("a").click(function(){ $(this).parent("li").click(); return false; }); /* 菜单项 接受单击 */ this.find("li").click(function(){ /* * 当单击菜单项 * 1.触发用户自定义的单击事件,将该 标签中的第一个超链接做为参数传递过去 * 2.修改当前菜单项所属的子菜单的显示状态(如果等于 true 将其设置为 false,否则将其设置为 true) * 3.重新初始化菜单 */ option.click($(this).find("a")[0]); /* 触发单击 */ /* * 如果当前节点下面包含子菜单,并且其 show 属性的值为 true,则修改其 show 属性为 false * 否则修改其 show 属性为 true */ /* if($(this).next("ul").attr("show")=="true"){ $(this).next("ul").attr("show","false"); }else{ $(this).next("ul").attr("show","true"); }*/ /* 初始化菜单 */ option._init(); }); /* 设置所有父节点样式 */ this.find("ul").prev("li").addClass("folder"); /* 设置节点“是否包含子节点”属性 */ this.find("li").find("a").attr("hasChild",false); this.find("ul").prev("li").find("a").attr("hasChild",true); /* 初始化菜单 */ option._init(); }/* SimpleTree Function End */ }); });
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

墩墩分墩

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

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

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

打赏作者

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

抵扣说明:

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

余额充值