Java通用tree树形结构,自定义注解,反射

前言

懒惰是技术的重要驱动力
现在大部分都是后端封装树形结构,第一次做的时候想从网上找通用,发现大部分都是要用类的get/set方法,不一样就要改,毕竟懒,改就改改,后来发现部门要,地区要,分类要,于是想着就把之前的用注解和反射做成通用的,当时改的下面这种的
在这里插入图片描述
改完之后在我的电脑上测试发现处理456条数据要3000毫秒左右(测试结果不同的电脑不一样),这。。。不行。
终于在今天凌晨4点的时候灵光一闪,把所有的数据遍历不到两遍就可以,

先说思路

在这里插入图片描述

在上代码

注解类:

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * @author ***
 * @version 1.0
 * @date 2020/7/28 16:12
 * 树形封装注解
 * value:id     主键id
 *        pid   父级别id
 *        list  子列表
 * level:等级,由高到底,逗号分割
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(value = {ElementType.FIELD})
public @interface TreeType {
    //注解成员,default表示默认值
    public String value() default "";
    public String level() default "";
}

tree工具类

package com.mooc1993.common.utils;

import com.mooc1993.common.annotate.TreeType;
import org.springframework.util.StringUtils;

import java.beans.IntrospectionException;
import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;
import java.lang.reflect.Field;

/**
 * @author ***
 * @version 1.0
 * @date 2020/7/28 15:47
 */
public class TreeObjectUtils<T> {
    //主键id字段名
    private String id;
    //父级id字段名
    private String parentId;
    //子级集合字段名
    private String child;
    //等级集合,从高到底。如:1,2,3
    private List<String> level;
    //等级字段名
    private String levelName;
    //key父id
    //value索引
    private Map<Object, Integer> map = null;
	/**
     * 获取树形结构
     * @param list
     * @param <T>
     * @param <E>
     * @return
     */
    public <T, E> List<T> get1(List<T> list) {
        if (null == list) {
            return list;
        }
        int size = list.size();
        if (size == 0) {
            return list;
        }
        getType(list.get(0));
        map = new HashMap<>(list.size());
        int levelSize = level.size();
        Map<Object, List<T>> levelMap = new HashMap<>(levelSize);
        for (int i = 0; i < size; i++) {
            T t = list.get(i);
            Object key = getName(id, t, null);
            Object mapLevel = getName(levelName, t, null);
            List<T> levelLists = levelMap.get(mapLevel);
            Integer index = null;
            if (null == levelLists) {
                List<T> levelList = new ArrayList<>();
                index = 0;
                levelList.add(t);
                levelMap.put(mapLevel, levelList);
            } else {
                index = levelLists.size();
                levelLists.add(t);
                levelMap.put(mapLevel, levelLists);
            }
            map.put(key, index);
        }
        for (int i = levelSize - 1; i > 0; i--) {
            List<T> levelLists = levelMap.get(level.get(i));
            int size1 = levelLists.size();
            for (int j = 0; j < size1; j++) {
                T unit = levelLists.get(j);
                //获取父级id
                Object key = getName(parentId, unit, null);
                //获取索引id
                Integer integer = map.get(key);
                //获取上一级对象
                List<T> tList = levelMap.get(level.get(i - 1));
                T t = tList.get(integer);
                List childList = (List) getName(child, t, null);
                if (null == childList) {
                    childList = new ArrayList<>();
                }
                childList.add(unit);
                getName(child, t, childList);
            }
        }
        return levelMap.get(level.get(0));
    }

    /**
     * 通过反射获取注解对应的字段名
     *
     * @param t
     */
    public <T> void getType(T t) {
        // 反射获得 class
        Class<?> clazz = t.getClass();
        // 如果类  上有注解 @TreeType ,取出注解 value的值
        TreeType declaredAnnotation = clazz.getDeclaredAnnotation(TreeType.class);
        if (declaredAnnotation != null) {
            System.out.println("类的注解@TreeType的value值是:" + declaredAnnotation.value());
        }
        //获得所有该类方法,不包括从其他地方继承过来的
        Field[] declaredFields = clazz.getDeclaredFields();

        for (Field declaredField : declaredFields) {
            TreeType fieldAnnotation = declaredField.getDeclaredAnnotation(TreeType.class);
            if (fieldAnnotation != null) {
                String value = fieldAnnotation.value();
                String level = fieldAnnotation.level();
                String name = declaredField.getName();
                //首字母转大写,拼接get,set方法。反射调用不拼接,不使用
                //name=toUpperCaseFirstOne(name);
                if ("id".equals(value)) {
                    this.id = name;
                } else if ("pid".equals(value)) {
                    this.parentId = name;
                } else if ("list".equals(value)) {
                    this.child = name;
                } else if (!StringUtils.isEmpty(level)) {
                    this.level = Arrays.asList(level.split(","));
                    this.levelName = name;
                }
            }
        }
    }

    /**
     * 通过反射调用方法获取参数
     *
     * @param name 方法名
     * @param obj  类
     * @param arg  参数
     * @return
     */
    private Object getName(String name, Object obj, Object arg) {
        PropertyDescriptor pd = null;
        try {
            pd = new PropertyDescriptor(name, obj.getClass());
        } catch (IntrospectionException e) {
            e.printStackTrace();
        }
        //获取set方法
        Method getMethod = pd.getReadMethod();
        //获取get方法
        Method getMethod1 = pd.getWriteMethod();
        Object rtn = null;
        try {
            if (arg == null) {
                rtn = getMethod.invoke(obj);
            } else {
                getMethod1.invoke(obj, arg);
            }

        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
        return rtn;
    }
}

例子:

@Data
public class FcSkillsType implements Serializable {
    @TreeType("id")
    private Integer typeId;

    private String typeName;
    
    @TreeType(level = "1,2,3,4,5")
    private String typeLevel;
    
    @TreeType("pid")
    private Integer typeFatherId;

    private String skillYpeIcon;

    private Boolean isDelete;
    
    @TreeType("list")
    private List<FcSkillsType> typeList;
    }

测试

用这种思路加反射和注解测试处理456条数据大概要30毫秒左右,如果不使用注解和反射,直接类.属性大概率1毫秒。

不足

工具还有好多不足和需要优化的地方,
比如等级要提前写入,不能根据数据自动扩展,这里我稍微处理了一下,假如预计会分3个等级(1,2,3),你可以在注解上多写几个,比如1,2,3,4,5,他不会影响代码的运行
还有对反射获取到的参数没有校验
注意反射获得所有该类方法,不包括从其他地方继承过来的
等等…

最终

工具没有做太多的测试,如果您发现了问题和不足,请及时联系我做修改,谢谢

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值