cloud-security-wiki/docs/util/tree-util/README.md

4.7 KiB

树形结构转换工具类

功能介绍

  • 将 A类的List数组快速转换为 B类型 List 的树形结构列表

实现原理

  • 主要使用到Java反射机制,将A类中的字段拷贝到B类中,并且根据A类中id 与 pid之间的关系快速构建树形结构
  • 设置配置类,更方便的实现A类与B类之前转换的需要,配置类包含有id字段与pid字段之间自定义设置,A类中需要转换到B类中的字段,以及是需要Spring 中 BeanUtils直接copy,以及如果使用了BeanUtil后需要排除的字段

代码展示

/**
 * @author Clay
 * @date 2022/11/16
 */
public class TreeUtil {

    private static TreeConfig config;

    private static Class<?> targetClass;

    /**
     * 构建tree结构
     *
     * @param list
     * @param target
     * @param node
     * @param <T>
     * @return
     */
    public static <T> List<T> build(List<?> list, Class<T> target, Node node) {
        //初始化config
        config = new TreeConfig();
        //将目标class对象设置为全局对象
        TreeUtil.targetClass = target;
        //提供给实现类对config进行修改
        node.build(config);
        //获取到最小的
        Object min = list.stream().min(Comparator.comparing(object -> getParentId(object).toString())).get();
        //获取到最小的父级id
        Object minPid = getParentId(min);
        //将数据通过他们的各自的父id进行分组
        Map<Object, List<Object>> listMap = list.stream().collect(Collectors.groupingBy(object -> getParentId(object)));
        //最终开始进行tree的构建
        return getChildren(listMap, minPid);
    }

    /**
     * 获取到子节点
     *
     * @param listMap
     * @param parentId
     * @param <T>
     * @return
     */
    private static <T> List<T> getChildren(Map<Object, List<Object>> listMap, Object parentId) {
        //获取到缓存中的list
        List<?> objects = listMap.get(parentId);
        if (chickList(objects)) {
            return null;
        }
        listMap.remove(parentId);
        //遍历数组,并将对象一一的转换
        List<Object> collect = objects.stream().map(object ->
                conversion(object, listMap)).collect(Collectors.toList());
        return (List<T>) collect;
    }

    /**
     * 对象直接的字段进行转换
     *
     * @param object
     * @param listMap
     * @param <T>
     * @return
     */
    private static <T> T conversion(Object object, Map<Object, List<Object>> listMap) {
        try {
            Object targetObject = targetClass.newInstance();
            //相同字段名称直接赋值
            if (config.isCopy()){
                BeanUtils.copyProperties(object, targetObject);
            }
            //不同字段名进行赋值
            for (Map.Entry<String, String> entry : config.getMapper().entrySet()) {
                String targetKey = entry.getKey();
                String sourceKey = entry.getValue();
                Object value = ReflectUtils.invokeGetter(object, sourceKey);
                Object[] args = new Object[]{value};
                ReflectUtils.invokeSetter(targetObject, targetKey, args);
            }
            //设置子节点
            Object id = ReflectUtils.invokeGetter(object, config.getIdField());
            List<Object> children = getChildren(listMap, id);
            if (!chickList(children)) {
                ReflectUtils.invokeSetter(targetObject, config.getChildrenField(), new Object[]{children});
            }
            //设置完毕后,将需要排除的字段进行置空
            for (String key : config.getExclude()) {
                ReflectUtils.invokeSetNull(targetObject, key);
            }
            //返回结果
            return (T) targetObject;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 检查list是否为空
     *
     * @param objects
     * @return
     */
    private static boolean chickList(List<?> objects) {
        return null == objects || objects.isEmpty();
    }

    /**
     * 获取到父级id
     *
     * @param object
     * @return
     */
    private static Object getParentId(Object object) {
        try {
            return ReflectUtils.invokeGetter(object, config.getParentField());
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

}

使用demo

@Override
public List<DeptVo> selectDeptTree(String deptName, Integer state) {
    List<Dept> deptList = deptMapper.selectDeptList(deptName, state);
    return TreeUtil.build(deptList,DeptVo.class,(config)->{
        config.setIdField("deptId");
        config.setMapper("key","deptId");
        config.setExclude("phone");
    });
}