Java 生成树结构(三)

TreeUtils.java

package com.pit.dds.utils.tree;

import cn.hutool.core.util.StrUtil;
import org.springframework.util.StringUtils;
import javax.validation.constraints.NotNull;
import java.lang.reflect.Field;
import java.util.*;
import java.util.stream.Collectors;

public class TreeUtil{

    /**
     * 转树结构
     * @param treeList
     * @return
     */
    public static List<Tree> getZreeList(List<? extends Tree> treeList) {
        if(treeList == null || treeList.size() == 0)
        {
            return new ArrayList<>();
        }
        List<Tree> parents = getParents(treeList);
        for (Tree p : parents) {
            List<? extends Tree> childPerms = getChildPerms(treeList, p.getId());
            p.setChildren(childPerms);
        }
        return parents;
    }

    /**
     * 获取顶层父节点id
     * @param treeList
     * @return
     */
    private static List<Tree> getParents(List<? extends Tree> treeList) {
        if (treeList == null || treeList.size() < 1) {
            return Collections.emptyList();
        }
        List<String> ids = treeList.stream().map(i -> i.getId())
                .filter(StrUtil::isNotEmpty).distinct()
                .collect(Collectors.toList());
        //pid在ids里面找不到的
        return treeList.stream().filter(i -> !ids.contains(i.getPId()))
                .collect(Collectors.toList());
    }

    /**
     * 根据父节点的ID获取所有子节点
     * @param list 分类表
     * @param pid  传入的父节点ID
     * @return String
     */
    private static List<? extends Tree> getChildPerms(List<? extends Tree> list, String pid) {
        pid = null == pid ? "" : pid;
        List<Tree> returnList = new ArrayList<>();
        for (Tree tree : list) {
            if (pid.equals(tree.getPId())) {
                returnList.add(tree);
                recursionFn(list, tree);
            }
        }
        return returnList;
    }

    /**
     * 得到子节点列表
     */
    private static List<? extends Tree> getChildList(List<? extends Tree> list, Tree t) {
        List<Tree> clist = new ArrayList<>();
        for (Tree tree : list) {
            if (t.getId().equals(tree.getPId())) {
                clist.add(tree);
            }
        }
        return clist;
    }

    /**
     * 判断是否有子节点
     */
    private static boolean hasChild(List<? extends Tree> list, Tree t) {
        for (Tree tree : list) {
            if (t.getId().equals(tree.getPId())) {
                return true;
            }
        }
        return false;
    }

    /**
     * 递归列表
     * @param list
     * @param t
     */
    private static void recursionFn(List<? extends Tree> list, Tree t) {
        List<? extends Tree> childList = getChildList(list, t);
        t.setChildren(childList);
        for (Tree tChild : childList) {
            if (hasChild(list, tChild)) {
                for (Tree tree : childList) {
                    recursionFn(list, tree);
                }
            }else{
                tChild.setChildren(new ArrayList<>());
            }
        }
    }

    /**
     * 集合转树结构
     *
     * @param collection 目标集合
     * @param clazz      集合元素类型
     * @return 转换后的树形结构
     */
    public static <T> Collection<T> toTree(@NotNull Collection<T> collection, @NotNull Class<T> clazz) {
        return toTree(collection, null, null, null, clazz);
    }

    /**
     * 集合转树结构
     * @param collection 目标集合
     * @param id         节点编号字段名称
     * @param parent     父节点编号字段名称
     * @param children   子节点集合属性名称
     * @param clazz      集合元素类型
     * @return           转换后的树形结构
     */
    public static <T> Collection<T> toTree(@NotNull Collection<T> collection, String id, String parent, String children, @NotNull Class<T> clazz) {
        try {
            if (collection == null || collection.isEmpty()) return null;// 如果目标集合为空,直接返回一个空树
            if (StringUtils.isEmpty(id)) id = "id";                     // 如果被依赖字段名称为空则默认为id
            if (StringUtils.isEmpty(parent)) parent = "parent";         // 如果依赖字段为空则默认为parent
            if (StringUtils.isEmpty(children)) children = "children";   // 如果子节点集合属性名称为空则默认为children

            // 初始化根节点集合, 支持 Set 和 List
            Collection<T> roots;
            if (collection.getClass().isAssignableFrom(Set.class)) {
                roots = new HashSet<>();
            } else {
                roots = new ArrayList<>();
            }

            // 获取 id 字段, 从当前对象或其父类
            Field idField;
            try {
                idField = clazz.getDeclaredField(id);
            } catch (NoSuchFieldException e1) {
                idField = clazz.getSuperclass().getDeclaredField(id);
            }

            // 获取 parentId 字段, 从当前对象或其父类
            Field parentField;
            try {
                parentField = clazz.getDeclaredField(parent);
            } catch (NoSuchFieldException e1) {
                parentField = clazz.getSuperclass().getDeclaredField(parent);
            }

            // 获取 children 字段, 从当前对象或其父类
            Field childrenField;
            try {
                childrenField = clazz.getDeclaredField(children);
            } catch (NoSuchFieldException e1) {
                childrenField = clazz.getSuperclass().getDeclaredField(children);
            }

            // 设置为可访问
            idField.setAccessible(true);
            parentField.setAccessible(true);
            childrenField.setAccessible(true);

            // 找出所有的根节点
            for (T c : collection) {
                Object parentId = parentField.get(c);
                if (isRootNode(parentId)) {
                    roots.add(c);
                }
            }

            // 从目标集合移除所有根节点
            collection.removeAll(roots);

            // 遍历根节点, 依次添加子节点
            for (T root : roots) {
                addChild(root, collection, idField, parentField, childrenField);
            }

            // 关闭可访问
            idField.setAccessible(false);
            parentField.setAccessible(false);
            childrenField.setAccessible(false);

            return roots;
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }

    /**
     * 为目标节点添加孩子节点
     *
     * @param node             目标节点
     * @param collection    目标集合
     * @param idField       ID 字段
     * @param parentField   父节点字段
     * @param childrenField 字节点字段
     */
    private static <T> void addChild(@NotNull T node, @NotNull Collection<T> collection, @NotNull Field idField, @NotNull Field parentField, @NotNull Field childrenField) throws IllegalAccessException {
        Object id = idField.get(node);
        Collection<T> children = (Collection<T>) childrenField.get(node);
        // 如果子节点的集合为 null, 初始化孩子集合
        if (children == null) {
            if (collection.getClass().isAssignableFrom(Set.class)) {
                children = new HashSet<>();
            } else children = new ArrayList<>();
        }

        for (T t : collection) {
            Object o = parentField.get(t);
            if (id.equals(o)) {
                // 将当前节点添加到目标节点的孩子节点
                children.add(t);
                // 重设目标节点的孩子节点集合,这里必须重设,因为如果目标节点的孩子节点是null的话,这样是没有地址的,就会造成数据丢失,所以必须重设,如果目标节点所在类的孩子节点初始化为一个空集合,而不是null,则可以不需要这一步,因为java一切皆指针
                childrenField.set(node, children);
                // 递归添加孩子节点
                addChild(t, collection, idField, parentField, childrenField);
            }
        }
    }

    /**
     * 判断是否是根节点, 判断方式为: 父节点编号为空或为 0, 则认为是根节点. 此处的判断应根据自己的业务数据而定.
     * @param parentId      父节点编号
     * @return              是否是根节点
     */
    private static boolean isRootNode(Object parentId) {
        boolean flag = false;
        if (parentId == null) {
            flag = true;
        } else if (parentId instanceof String && (StringUtils.isEmpty(parentId) || parentId.equals("0"))) {
            flag = true;
        } else if (parentId instanceof Integer && Integer.valueOf(0).equals(parentId)) {
            flag = true;
        }
        return flag;
    }
}

基础Tree.java

package com.pit.dds.utils.tree;

import java.util.List;
/**
 * 树结构接口
 */
public interface Tree {

    String getId();

    String getPId();

    List<? extends Tree> getChildren();

    void setChildren(List<? extends Tree> list);

}

自定义Tree实体.java

PitDdsDrawingCatalogTree 继承对应的业务实体,实现返回的树结构包含业务表的数据。 再实现Tree.java

package com.pit.dds.drawing.vo;

import cn.hutool.core.convert.Convert;
import com.pit.dds.drawing.entity.PitDdsDrawingCatalogEntity;
import com.pit.dds.utils.tree.Tree;
import lombok.Data;

import java.util.List;

@Data
public class PitDdsDrawingCatalogTree extends PitDdsDrawingCatalogEntity implements Tree {

    List<? extends Tree> Children;

    @Override
    public String getId() {
    	//取主键ID
        return super.getDrawingCatalogId();
    }

    @Override
    public String getPId() {
    	//取父级ID
        return Convert.toStr(super.getUpDrawingCatalogId());
    }

    Boolean IsOpen = true;

}

业务实现

1、mybatis查询 resultType用自定义的PitDdsDrawingCatalogTree实体接收
mybatis查询 resultType用自定义的Tree实体接收
2、TreeUtil.getZreeList(ztreeList);
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值