Java集合List转树结构工具类[通俗易懂]

Java集合List转树结构工具类[通俗易懂]业务场景:菜单树、组织架构树…..前端要求数据结构为树结构,而后端查出来的是一条一条的数据集,每次都要各种递归遍历很麻烦,特此写了一个工具类来解决.三个注解:importjava.lang.annotation.ElementType;importjava.lang.annotation.Retention;importjava.lang.annotation.RetentionPolicy;importjava.lang.annotation.Target;/***@a

大家好,又见面了,我是你们的朋友全栈君。

此版本太累赘,请转到函数版:https://blog.csdn.net/wenxingchen/article/details/115749782?spm=1001.2014.3001.5501

业务场景:菜单树、组织架构树…..前端要求数据结构为树结构,而后端查出来的是一条一条的数据集,每次都要各种递归遍历很麻烦,特此写了一个工具类来解决.

  • 三个注解:
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * @author sunziwen
 * @since 2021-4-13 16:19:05
 */
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface TreeId {
}
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * @author sunziwen
 * @since 2021-4-13 16:19:05
 */
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface TreeParentId {
}
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * @author sunziwen
 * @since 2021-4-13 16:19:05
 */
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface TreeChildren {
}

一个工具类:

 

import cn.hutool.core.util.StrUtil;
import lombok.SneakyThrows;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 树形工具类
 *
 * @author sunziwen
 * @since 2021-4-13 16:19:05
 */

public class TreeUtil {
    /**
     * 找出顶层节点
     *
     * @return data
     */
    @SneakyThrows
    private <T> List<T> treeOut(List<T> list) {
        //数据不能为空
        if (list == null || list.size() <= 0) {
            return list;
        }
        //获取泛型T的class
        Class<?> aClass = list.get(0).getClass();

        Field[] declaredFields = aClass.getDeclaredFields();
        //获取主键属性
        List<Field> idPropertyField = Arrays.stream(declaredFields).filter(x -> {
            TreeId annotation = x.getAnnotation(TreeId.class);
            return annotation != null;
        }).collect(Collectors.toList());
        if (idPropertyField.size() <= 0) {
            throw new RuntimeException("缺失@TreeId注解");
        }
        if (idPropertyField.size() > 1) {
            throw new RuntimeException("@TreeId注解只能存在一个");
        }
        //获取父节点属性
        List<Field> parentIdPropertyField = Arrays.stream(declaredFields).filter(x -> {
            TreeParentId annotation = x.getAnnotation(TreeParentId.class);
            return annotation != null;
        }).collect(Collectors.toList());
        if (parentIdPropertyField.size() <= 0) {
            throw new RuntimeException("缺失@ParentId注解");
        }
        if (parentIdPropertyField.size() > 1) {
            throw new RuntimeException("@ParentId注解只能存在一个");
        }

        /*主键的属性名*/
        String idPropertyName = idPropertyField.get(0).getName();
        /*主键的get方法*/
        Method getId = aClass.getMethod("get" + StrUtil.upperFirst(idPropertyName));

        /*父节点的属性名*/
        String parentIdPropertyName = parentIdPropertyField.get(0).getName();
        /*父节点的get方法*/
        Method getParentId = aClass.getMethod("get" + StrUtil.upperFirst(parentIdPropertyName));

        /*所有元素的Id*/
        List<Object> ids = list.stream().map(x -> {
            try {
                return getId.invoke(x);
            } catch (IllegalAccessException | InvocationTargetException e) {
                e.printStackTrace();
            }
            return null;
        }).collect(Collectors.toList());
        /*查出所有顶级节点*/
        List<T> topLevel = list.stream().filter(x -> {
            try {
                return !ids.contains(getParentId.invoke(x));
            } catch (IllegalAccessException | InvocationTargetException e) {
                e.printStackTrace();
            }
            return false;
        }).collect(Collectors.toList());

        return recursion(topLevel, list);
    }


    /**
     * 递归装载
     *
     * @param superLevel 上级节点
     * @param list       数据集
     * @return
     */
    @SneakyThrows
    private <T> List<T> recursion(List<T> superLevel, List<T> list) {
        //获取泛型T的class
        Class<?> aClass = list.get(0).getClass();

        Field[] declaredFields = aClass.getDeclaredFields();
        //获取主键属性
        List<Field> idPropertyField = Arrays.stream(declaredFields).filter(x -> {
            TreeId annotation = x.getAnnotation(TreeId.class);
            return annotation != null;
        }).collect(Collectors.toList());
        if (idPropertyField.size() <= 0) {
            throw new RuntimeException("缺失@TreeId注解");
        }
        if (idPropertyField.size() > 1) {
            throw new RuntimeException("@TreeId注解只能存在一个");
        }
        //获取父节点属性
        List<Field> parentIdPropertyField = Arrays.stream(declaredFields).filter(x -> {
            TreeParentId annotation = x.getAnnotation(TreeParentId.class);
            return annotation != null;
        }).collect(Collectors.toList());
        if (parentIdPropertyField.size() <= 0) {
            throw new RuntimeException("缺失@ParentId注解");
        }
        if (parentIdPropertyField.size() > 1) {
            throw new RuntimeException("@ParentId注解只能存在一个");
        }

        //获取父节点属性
        List<Field> childrenPropertyField = Arrays.stream(declaredFields).filter(x -> {
            TreeChildren annotation = x.getAnnotation(TreeChildren.class);
            return annotation != null;
        }).collect(Collectors.toList());
        if (childrenPropertyField.size() <= 0) {
            throw new RuntimeException("缺失@TreeChildren注解");
        }
        if (childrenPropertyField.size() > 1) {
            throw new RuntimeException("@TreeChildren注解只能存在一个");
        }

        /*主键的属性名*/
        String idPropertyName = idPropertyField.get(0).getName();
        /*主键的get方法*/
        Method getId = aClass.getMethod("get" + StrUtil.upperFirst(idPropertyName));

        /*父节点的属性名*/
        String parentIdPropertyName = parentIdPropertyField.get(0).getName();
        /*父节点的get方法*/
        Method getParentId = aClass.getMethod("get" + StrUtil.upperFirst(parentIdPropertyName));

        /*子节点的属性名*/
        String childrenPropertyName = childrenPropertyField.get(0).getName();
        /*字节点的set方法*/
        Method setChildren = aClass.getMethod("set" + StrUtil.upperFirst(childrenPropertyName));


        for (T t : superLevel) {
            List<T> children = list.stream().filter(x -> {
                try {
                    return getParentId.invoke(x).equals(getId.invoke(t));
                } catch (IllegalAccessException | InvocationTargetException e) {
                    e.printStackTrace();
                }
                return false;
            }).collect(Collectors.toList());
            if (children.size() <= 0) {
                continue;
            }
            List<T> recursion = recursion(children, list);
            setChildren.invoke(t, recursion);
        }
        return superLevel;
    }
}
  • 使用示例:
  • 
    import lombok.Data;
    
    import java.util.List;
    
    @Data
    public class My {
        @TreeId//在实体类的主键上打上该注解
        private String id;
    
        @TreeParentId//在实体类的父节点id上打上该注解
        private String parentId;
    
        private String name;
    
        @TreeChildren //在子集上打上该注解
        //@TableField(exist = false)//如果你用的是mybatis-plus则需要让框架忽略该字段
        private List<My> children;
    
        public My(String id, String parentId, String name) {
            this.id = id;
            this.parentId = parentId;
            this.name = name;
        }
    }
        public static void main(String[] args) {
            ArrayList<My> mies = new ArrayList<>();
            mies.add(new My("1", "-1", "a"));
            mies.add(new My("2", "-1", "aa"));
            mies.add(new My("3", "1", "b"));
            mies.add(new My("4", "1", "c"));
            mies.add(new My("5", "3", "d"));
            mies.add(new My("6", "5", "e"));
            mies.add(new My("7", "6", "f"));
            mies.add(new My("8", "2", "g"));
            mies.add(new My("9", "8", "h"));
            mies.add(new My("10", "9", "i"));
            List<My> mies1 = TreeUtil.treeOut(mies);
            System.out.println(mies1);
        }

    大功告成了,如果有问题请加博主V:sunziwen3366

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/160344.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • 视频教程-SpringBoot实战视频教程-Java

    视频教程-SpringBoot实战视频教程-JavaSpringBoot实战视频教程拥有5年的java后端开发经验,熟悉行ja…

  • GoLand 2022.01.21 激活码_在线激活2022.02.01「建议收藏」

    (GoLand 2022.01.21 激活码)好多小伙伴总是说激活码老是失效,太麻烦,关注/收藏全栈君太难教程,2021永久激活的方法等着你。https://javaforall.cn/100143.htmlIntelliJ2021最新激活注册码,破解教程可免费永久激活,亲测有效,上面是详细链接哦~CJM5ZJBPHS-eyJsaWNlbnNlSWQiOi…

  • struts2的response的contentType设置

    struts2的response的contentType设置服务器端发送到客户端,提示保存还是打开?       response.setContentType(“text/html;charset=utf-8”)发送到客户端格式不正确。使用ajaxUpload插件的时候,strust2返回application/json格式的数据,但是ajaxUpload要求返回text/html,这样就需要在配置文件中配置contentType项。

  • python强制类型转换astype

    python强制类型转换astype在进行将多个表的数据合并到一个表后,发现输出到EXCEL表的数据发生错误,数值型数据末尾都变成了0。这是因为excel数据超过11位,自动以科学计数法显示,其最大处理精度为15位,超过15位,以后数字自动变0。找了一些解决方法,发现用.astype(‘数据类型’)还是挺方便的。我在输出时,将数值型的数据(int)转化成了字符串(str)。使用方法:df.astype(‘数据类型’)  …

  • pycharm断点调试教程_pycharm怎么debug

    pycharm断点调试教程_pycharm怎么debug前言如果你不会用IDE开发工具的debug,你在调试代码的时候可能会用print输出去调试,那样效率比较低。我们可以用Pycharm的debug来调试,当然如果你用的Jetbranis的其他产品,操作方法也是一样的。Pycharm的Debug(1)开启debug的方式:右键debug项目 工具栏的甲壳虫(2)常用按钮图解debugger栏:stepover(单步调试)程序代码越过子函数,但子函数会执行,且不进入。 stepinto(进入)在单步执行时,遇到子函数就进入.

  • tcp攻击脚本_防御的意思

    tcp攻击脚本_防御的意思目录相关原理(tcp基础)实例演示关于防御措施相关原理(tcp基础)三次握手:TCP是基于IP网络层之上的传输层协议,用于端到端的可靠的字节流传输。过程:1.C向S发送连接请求,标记位SYN设为1,且随机设置序列号seq2.S返回确认消息,ACK设为seq+1,标记位SYN设为1,随机序列号seq3.C返回确认消息,ACK设为seq+1四次挥手:四次挥手指正常连接中断的情况。过程:…

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号