java使用注解方式实现树形结构数据

267 阅读2分钟

文章介绍

本文将数据list转成树形结构数据。 必要条件:实体类结构必须有基本树形数据结构必要字段 例如:节点id字段、父节点id字段、子 节点集合字段。

自定义注解

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

/**
 *  通过注解构建实体类树形数据结构
 *
 *  只需在实体类中 节点字段、父节点字段、子节点集合字段 加上本注解,并填充 type属性值即可
 *  节点字段: 支持类型 String、Long 、int 、Integer
 *  父节点字段: 支持类型 String、Long 、int 、Integer
 *  子集合字段: 支持类型  List<实体类>、List<Object>
 */
@Target(value= {ElementType.FIELD})
@Documented
@Retention(value = RetentionPolicy.RUNTIME)
public @interface TreeElement {

    /**
     * 字段类型 引用 TreeColumnType 中的常量
     * @return
     */
    String type() default "";
}

使用到的常量接口

/**
 * @TreeElement注解type属性 标注值
 */
public interface TreeColumnType {

    /**
     * 节点id
     */
    String NODE_ID = "NODE_ID";

    /**
     * 父节点id
     */
    String PARENT_ID = "PARENT_ID";

    /**
     * 子节点集合
     */
    String CHILDREN_ARRAY = "CHILDREN_ARRAY";
}

树形数据构建工具类

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

public class TreeUtils {

    /**
     * 将列表转换为列表树/Convert List to List Tree
     * @param trees 生成树形结构数据源
     * @param boot 子集合无数据时 boot等于true返回null,boot等于false返回空数组
     * @param clas 树形数据结构对象 即 @TreeElement 注解的使用类
     * @param maxDeep 最大递归深度 为null则不限制
     * @return result
     * @throws IllegalAccessException result
     */
    public static <T> List<T> ListToTree(List<T> trees,boolean boot,Integer maxDeep,Class<T> clas) throws IllegalAccessException, NoSuchFieldException {
        HashMap<String,String> keys = findTreeElement(clas);
        List<Object> ids = findColumnValues(trees,keys.get(TreeColumnType.NODE_ID));
        List<T> rootList = new ArrayList<T>();
        HashMap<Object, List<T>> pidAndTrees= new HashMap<>();
        Object value = null;
        for (final T t : trees) {
             value = getValue(t, keys.get(TreeColumnType.PARENT_ID));
            if(value!=null){
                if(pidAndTrees.get(value)!=null) {
                    pidAndTrees.get(value).add(t);
                }else{
                    pidAndTrees.put(value,new ArrayList<T>(){{this.add(t);}});
                }
                if (!ids.contains(value)) {
                    rootList.add(t);
                }
            }
        }
        buildChilTree(rootList,pidAndTrees,keys.get(TreeColumnType.NODE_ID),keys.get(TreeColumnType.CHILDREN_ARRAY), boot,0,maxDeep);
        return rootList;
    }

    /**
     * 获取指定实体类 @TreeElement注解标注的字段名称
     * @param clas
     * @param <T>
     * @return
     */
    private static  <T> HashMap<String,String> findTreeElement(Class<T> clas){
        Field[] fields = clas.getDeclaredFields();
        HashMap<String,String> keys = new HashMap<>(3);
        TreeElement treeElement = null;
        for (Field field : fields) {
             treeElement = field.getAnnotation(TreeElement.class);
            if (null != treeElement){
                switch (treeElement.type()){
                    case TreeColumnType.NODE_ID:
                        keys.put(TreeColumnType.NODE_ID,field.getName());
                        break;
                    case TreeColumnType.PARENT_ID:
                        keys.put(TreeColumnType.PARENT_ID,field.getName());
                        break;
                    case TreeColumnType.CHILDREN_ARRAY:
                        keys.put(TreeColumnType.CHILDREN_ARRAY,field.getName());
                        break;
                    default:
                        break;
                }
            }
            if (keys.size()==3){
                break;
            }
        }
        return keys;
    }

    /**
     *  提取数据源中指定字段值
     * @param ts 源数据集合
     * @param key 需提取值的字段名称
     * @param <T>
     * @return
     * @throws IllegalAccessException
     * @throws NoSuchFieldException
     */
    private static  <T>  List<Object> findColumnValues(List<T> ts,String key) throws IllegalAccessException, NoSuchFieldException {
        List<Object> ids = new ArrayList<>();
        Field field = null;
        for (T t : ts) {
            field = t.getClass().getDeclaredField(key);
            field.setAccessible(true);
            ids.add(field.get(t));
        }
        return ids;
    }

    /**
     * 获取与对象的指定注释值对应的字段值
     * Get the field value corresponding to the specified annotation value of the object
     * @param t
     * @param key
     * @param <T>
     * @return
     * @throws IllegalAccessException
     */
    private static <T> Object getValue(T t,String key) throws IllegalAccessException, NoSuchFieldException {
        Field field = t.getClass().getDeclaredField(key);
        field.setAccessible(true);
        Object o = field.get(t);
        return o;
    }
    /**
     * 设置与对象的指定注释值对应的字段值
     * Get the field value corresponding to the specified annotation value of the object
     * @param t
     * @param key
     * @param <T>
     * @return
     * @throws IllegalAccessException
     */
    private static <T> Object setValue(T t,String key,boolean boot) throws IllegalAccessException, NoSuchFieldException {
        Field field = t.getClass().getDeclaredField(key);
        field.setAccessible(true);
        field.set(t, boot ? null:new ArrayList<>());
        return null;
    }
    /**
     * 获取对象的指定字段对应的字段类型
     * Get the field type corresponding to the specified field of the object
     * @param t
     * @param key
     * @param <T>
     * @return
     * @throws IllegalAccessException
     */
    public static <T> Object getType(T t,String key) throws IllegalAccessException, NoSuchFieldException {
        Field field = t.getClass().getDeclaredField(key);
        field.setAccessible(true);
        return field.getType().toString();
    }

    /**
     * 递归构造/Recursive construction
     * @param currentTrees
     * @param trees
     * @param <T>
     * @param deep – 已递归深度
     * @param maxDeep – 最大递归深度 可能为null即不限制
     * @throws IllegalAccessException
     */
    private static <T> void buildChilTree(List<T> currentTrees, HashMap<Object, List<T>>  trees,String id,String children,boolean boot,int deep,Integer maxDeep) throws IllegalAccessException, NoSuchFieldException {

        if (maxDeep != null && deep >= maxDeep) {
            return;
        }
        Object currentId = null;
        for (T t : currentTrees) {
             currentId = getValue(t, id);
            if(trees.get(currentId)!=null){
                List list = (List) getValue(t, children);
                list.addAll(trees.get(currentId));
                buildChilTree(trees.get(currentId),trees,id,children,boot,deep+1,maxDeep);
            }else {
                setValue(t, children,boot);
            }
        }
    }

}

实体类样式

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

public class TestVo
{

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

    /** 节点id 等于0时代表一级根节点*/
    @TreeElement(type = TreeColumnType.NODE_ID)
    private Long id;
    
    /** 父节点id */
    @TreeElement(type = TreeColumnType.PARENT_ID)
    private Long parentId;

    /** 子节点集合 */
    @TreeElement(type = TreeColumnType.CHILDREN_ARRAY)
    private List<TbTownshipVillage> children = new ArrayList<>();

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

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

    public void setName(String name) 
    {
        this.name = name;
    }

    public String getName() 
    {
        return name;
    }
    public void setParentId(Long parentId) 
    {
        this.parentId = parentId;
    }

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

    public Long getId() 
    {
        return id;
    }
}

调用代码

List<TestVo> listVo = new ArrayList<>();  //listVo 中数据自行填充或从数据库查询
List<TestVo> list = TreeUtils.ListToTree(listVo, false,null,TestVo.class);