记一次JDK 1.8实现树结构

由于项目前端之前要做树结构的展示,后端为了展示树结构碰到一些问题,现特做记录,
问题一: 当数据库字段和树结构不匹配时怎么处理?
问题二:当传递根节点 为多节点怎么处理?
问题三:传递节点是否为叶子节点的处理
。。。 等等等等 不一一列表,下面用详细代码解释:

话不多说,先看结果,为了方便查看,都打印的JSON格式: 单根节点的处理
在这里插入图片描述

多根节点的处理:
在这里插入图片描述

public class TestTree {


    /*测试一个根节点树结构的入口*/
    @Test
    public void testOneRootTree() {
        //根节点ID
        Integer parentId = 1;

        /*初始化数据*/
        List<DeptDO> deptList = getDeptList();

        //实体类转换成树结构
        List<DeptTree> bodyList = deptList.stream().map(dept -> {
            DeptTree deptTree = new DeptTree();
            BeanUtils.copyBean(dept, deptTree);
            return deptTree;
        }).collect(Collectors.toList());


        //取root
        Optional<DeptTree> first = bodyList.stream()
                .filter(tree -> tree.getId().equals(parentId)).findFirst();
        DeptTree rootTree = null;
        if (first.isPresent()) {
            rootTree = first.get();
        }

        /*树处理*/
        TreeToolUtils treeToolUtils = new TreeToolUtils(rootTree, bodyList);
        List<DeptTree> tree = treeToolUtils.getTree();

        //return Objects.nonNull(tree)?tree.get(0):null;
        /*返回值*/
        System.out.println(JSON.toJSONString(tree.get(0)));
    }

	/*测试多根节点入口*/
    @Test
    public void testMultiRoot(){
        List<DeptDO> deptRootList = getRootList();

        //实体类转换成树结构
        List<DeptTree> rootList = deptRootList.stream().map(dept -> {
            DeptTree deptTree = new DeptTree();
            BeanUtils.copyBean(dept, deptTree);
            return deptTree;
        }).collect(Collectors.toList());

        /*初始化数据*/
        List<DeptDO> deptList = getDeptList();

        //实体类转换成树结构
        List<DeptTree> bodyList = deptList.stream().map(dept -> {
            DeptTree deptTree = new DeptTree();
            BeanUtils.copyBean(dept, deptTree);
            return deptTree;
        }).collect(Collectors.toList());

        /*树处理*/
        TreeToolUtils treeToolUtils = new TreeToolUtils(rootList, bodyList);
        List<DeptTree> tree = treeToolUtils.getTree();

        //返回值
        System.out.println(JSON.toJSONString(tree));
    }


    private List<DeptDO> getDeptList() {
        List<DeptDO> deptDOList = Lists.newArrayList();
        DeptDO deptDO1 = new DeptDO();
        deptDO1.setId(1);
        deptDO1.setParentId(0);
        deptDO1.setDeptName("主部门1");

        DeptDO deptDO2 = new DeptDO();
        deptDO2.setId(2);
        deptDO2.setParentId(1);
        deptDO2.setDeptName("子部门2");


        DeptDO deptDO3 = new DeptDO();
        deptDO3.setId(3);
        deptDO3.setParentId(1);
        deptDO3.setDeptName("子部门3");

        DeptDO deptDO4 = new DeptDO();
        deptDO4.setId(4);
        deptDO4.setParentId(2);
        deptDO4.setDeptName("子子部门4");



        DeptDO deptDO5 = new DeptDO();
        deptDO5.setId(5);
        deptDO5.setParentId(3);
        deptDO5.setDeptName("子子部门5");

        deptDOList.add(deptDO1);
        deptDOList.add(deptDO2);
        deptDOList.add(deptDO3);
        deptDOList.add(deptDO4);
        deptDOList.add(deptDO5);
        return deptDOList;
    }


    private List<DeptDO> getRootList() {
        List<DeptDO> deptDOList = Lists.newArrayList();
        DeptDO deptDO1 = new DeptDO();
        deptDO1.setId(2);
        deptDO1.setParentId(1);
        deptDO1.setDeptName("主部门2");

        DeptDO deptDO2 = new DeptDO();
        deptDO2.setId(3);
        deptDO2.setParentId(1);
        deptDO2.setDeptName("主部门3");

        deptDOList.add(deptDO1);
        deptDOList.add(deptDO2);
        return deptDOList;
    }

}
@Data
public class DeptDO {
    /*部门id*/
    @CopyField(targetName = "id")
    private Integer id;
    /*父部门的ID*/
    @CopyField(targetName = "pid")
    private Integer parentId;
    /*部门名称*/
    @CopyField(targetName = "title")
    private String deptName;
}
@Data
@ApiModel("基础树模型")
public class BaseTree<T extends BaseTree> {

    /**
     * 节点ID
     */
    @ApiModelProperty("节点ID")
    private Integer id;

    /**
     * 节点父ID
     */
    @ApiModelProperty("节点父ID")
    private Integer pid;

    /**
     * 节点标题
     */
    @ApiModelProperty("节点标题")
    private String title;

    /**
     * 是否没有叶子
     */
    @ApiModelProperty("是否叶子")
    private Boolean leaf = false;

    /**
     * 子级
     */
    @ApiModelProperty("子级")
    private List<T> children;

}
/*部门树,这里可扩展部门的明细字段*/
public class DeptTree extends BaseTree {
}

@Slf4j
public class BeanUtils extends org.springframework.beans.BeanUtils {
   


    /**
     * <h3>拷贝一个对象的属性至另一个对象</h3>
     * <p>
     * 支持两个对象之间不同属性名称进行拷贝,使用注解{@link CopyField}
     * </p>
     * @param originBean 源对象 使用注解{@link CopyField#targetName()}
     * @param targetBean 目标对象 使用注解{@link CopyField#originName()}
     */
    public static void copyBean(Object originBean, Object targetBean) {
        Map<String, Object> originFieldKeyWithValueMap = new HashMap<>(16);
        PropertyDescriptor propertyDescriptor = null;
        //生成源bean的属性及其值的字典
        generateOriginFieldWithValue(propertyDescriptor, originBean, originFieldKeyWithValueMap, originBean.getClass());
        //设置目标bean的属性值
        settingTargetFieldWithValue(propertyDescriptor, targetBean, originFieldKeyWithValueMap, targetBean.getClass());
    }

    /**
     * 生成需要被拷贝的属性字典 属性-属性值<br/>
     * 递归取父类属性值
     * @param propertyDescriptor         属性描述器,可以获取bean中的属性及方法
     * @param originBean                 待拷贝的bean
     * @param originFieldKeyWithValueMap 存放待拷贝的属性和属性值
     * @param beanClass                  待拷贝的class[可能是超类的class]
     */
    private static void generateOriginFieldWithValue(PropertyDescriptor propertyDescriptor, Object originBean, Map<String, Object> originFieldKeyWithValueMap, Class<?> beanClass) {
        /**如果不存在超类,那么跳出循环*/
        if (beanClass.getSuperclass() == null) {
            return;
        }
        Field[] originFieldList = beanClass.getDeclaredFields();
        for (Field field : originFieldList) {
            try {
                /*获取属性上的注解。如果不存在,使用属性名,如果存在使用注解名*/
                CopyField annotation = field.getAnnotation(CopyField.class);
                String targetName = "";
                if (annotation != null) {
                    targetName = annotation.targetName();
                } else {
                    targetName = field.getName();
                }
                /** 过滤serialVersionUID */
                if ("serialVersionUID".equals(targetName)) {
                    continue;
                }
                //初始化
                propertyDescriptor = new PropertyDescriptor(field.getName(), beanClass);
                //获取当前属性的get方法
                Method method = propertyDescriptor.getReadMethod();
                //设置值
                Object value = method.invoke(originBean);
                //设置值
                originFieldKeyWithValueMap.put(targetName, value);
            } catch (IntrospectionException e) {
                log.warn("【源对象】异常:" + field.getName() + "不存在对应的get方法,无法参与拷贝!");
            } catch (IllegalAccessException e) {
                log.warn("【源对象】异常:" + field.getName() + "的get方法执行失败!");
            } catch (InvocationTargetException e) {
                log.warn("【源对象】异常:" + field.getName() + "的get方法执行失败!");
            }
        }
        //生成超类 属性-value
        generateOriginFieldWithValue(propertyDescriptor, originBean, originFieldKeyWithValueMap, beanClass.getSuperclass());
    }

    /**
     * 设置目标对象属性
     * @param propertyDescriptor         属性描述器,获取当前传入属性的(getter/setter)方法
     * @param targetBean                 目标容器bean
     * @param originFieldKeyWithValueMap 待拷贝的属性和属性值
     * @param beanClass                  待设置的class[可能是超类的class]
     */
    private static void settingTargetFieldWithValue(PropertyDescriptor propertyDescriptor, Object targetBean, Map<String, Object> originFieldKeyWithValueMap, Class<?> beanClass) {
        /**如果不存在超类,那么跳出循环*/
        if (beanClass.getSuperclass() == null) {
            return;
        }
        Field[] targetFieldList = beanClass.getDeclaredFields();
        for (Field field : targetFieldList) {
            try {
                /*获取属性上的注解。如果不存在,使用属性名,如果存在使用注解名*/
                CopyField annotation = field.getAnnotation(CopyField.class);
                String originName = "";
                if (annotation != null) {
                    originName = annotation.originName();
                } else {
                    originName = field.getName();
                }
                /** 过滤serialVersionUID */
                if ("serialVersionUID".equals(originName) || !originFieldKeyWithValueMap.containsKey(originName)) {
                    continue;
                }
                //初始化当前属性的描述器
                propertyDescriptor = new PropertyDescriptor(field.getName(), beanClass);
                //获取当前属性的set方法
                Method method = propertyDescriptor.getWriteMethod();
                method.invoke(targetBean, originFieldKeyWithValueMap.get(originName));
            } catch (IntrospectionException e) {
                log.warn("【目标对象】异常:" + field.getName() + "不存在对应的set方法,无法参与拷贝!");
            } catch (IllegalAccessException e) {
                log.warn("【目标对象】异常:" + field.getName() + "的set方法执行失败!");
            } catch (InvocationTargetException e) {
                log.warn("【目标对象】异常:" + field.getName() + "的set方法执行失败!");
            }
        }
        //设置超类属性
        settingTargetFieldWithValue(propertyDescriptor, targetBean, originFieldKeyWithValueMap, beanClass.getSuperclass());
    }
}
/*树的工具类*/
public class TreeToolUtils<T extends BaseTree> {


    /**
     * 根节点对象存放到这里
     */
    private List<T> rootList;

    /**
     * 其他节点存放到这里,可以包含根节点
     */
    private List<T> bodyList;

    public TreeToolUtils(List<T> rootList, List<T> bodyList) {
        this.rootList = rootList;
        this.bodyList = bodyList;
    }


    public TreeToolUtils(T root, List<T> bodyList) {
        rootList = Lists.newArrayList();
        rootList.add(root);
        this.bodyList = bodyList;
    }

    public List<T> getTree() {   //调用的方法入口
        if (bodyList != null && !bodyList.isEmpty()) {
            //声明一个map,用来过滤已操作过的数据
            Map<Integer, Integer> map = Maps.newHashMapWithExpectedSize(bodyList.size());
            //传递根对象和一个空map
            rootList.forEach(beanTree -> getChild(beanTree, map));
            return rootList;
        }
        return null;
    }

    public void getChild(BaseTree beanTree, Map<Integer, Integer> map) {
        List<T> childList = Lists.newArrayList();
        bodyList.stream()
                //map内不包含子节点的code
                .filter(c -> !map.containsKey(c.getId()))
                //子节点的父id==根节点的code 继续循环
                .filter(c -> c.getPid().equals(beanTree.getId()))
                .forEach(c -> {
                    map.put(c.getId(), c.getPid());//当前节点code和父节点id
                    getChild(c, map);//递归调用
                    childList.add(c);
                });
        beanTree.setLeaf(childList.isEmpty());
        beanTree.setChildren(childList);
    }


}
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface CopyField {

    /**
     * 在即将被拷贝的属性上面,设置目标属性名
     */
    String targetName() default "";

    /**
     * 在即将拷贝至改属性上面,设置源属性名
     */
    String originName() default "";
}

不知道有没有复制完,有些是其他博客找的,源头找不到了,好用给个赞吧, 哈哈~~~

发布了14 篇原创文章 · 获赞 2 · 访问量 174

猜你喜欢

转载自blog.csdn.net/weixin_41402056/article/details/104835774
今日推荐