构建树结构(节点级别,全路径)

发布时间 2023-11-23 16:25:54作者: Aoul
package org.example.tree;

import org.springframework.util.CollectionUtils;

import java.util.*;

/**
 * @ClassName TreeUtils2
 * @Description TODO
 * @Author hrp
 * @Date 2023/11/23 14:39
 */
public class TreeUtils<N extends TreeNode<T, RC, LC>, T, RC, LC> {

    // 所有结点
    private List<N> nodeList;
    // 所有根节点
    private List<N> rootList;

    public TreeUtils(List<N> nodeList) {
        this.nodeList = new ArrayList<>(nodeList);
        this.rootList = new ArrayList<>();
    }

    public List<N> getTree() {
        return this.rootList;
    }

    /**
     * 根据所有树节点列表,按默认条件生成含有所有树形结构的列表
     * 主要用于组建简单树形结构
     *
     * @return 树形结构列表
     */
    public TreeUtils generateTrees() {
        // 使用迭代器操作list元素
        for (Iterator<N> iterator = this.nodeList.iterator(); iterator.hasNext(); ) {
            N node = iterator.next();
            if (node.isRoot()) {
                node.setLevel(0);
                node.setPath("/" + node.getId()+"/");
                // 获取所有的根节点
                rootList.add(node);
                // 从所有节点列表中删除该节点,以免后续重复遍历该节点
                iterator.remove();
            }
        }
        rootList.forEach(r -> {
            fillLeaves(r);
        });
        return this;
    }

    /**
     * 根据所有树节点列表,按自定义条件---->获取符合条件的父节点
     *
     * @return 按自定义条件获取符合条件的父节点
     */
    public TreeUtils setRoots() {
        rootList.clear();
        // 使用迭代器操作list元素
        for (Iterator<N> iterator = nodeList.iterator(); iterator.hasNext(); ) {
            N node = iterator.next();
            // 按条件筛选根节点
            if (node.isRoot()) {
                node.setLevel(0);
                // 获取所有的根节点
                rootList.add(node);

                // 从所有节点列表中删除该节点,以免后续重复遍历该节点
                iterator.remove();
            }
        }
        // 返回按条件查询到的父节点
        return this;
    }

    /**
     * 根据所有树节点列表,按自定义条件---->获取符合条件的父节点
     *
     * @param rootCondition 父节点的判定规则
     * @return 按自定义条件获取符合条件的父节点
     */
    public TreeUtils setRoots(RC rootCondition) {
        rootList.clear();
        // 使用迭代器操作list元素
        for (Iterator<N> iterator = nodeList.iterator(); iterator.hasNext(); ) {
            N node = iterator.next();
            // 按条件筛选根节点
            if (node.isRoot(rootCondition)) {
                node.setLevel(0);
                // 获取所有的根节点
                rootList.add(node);

                // 从所有节点列表中删除该节点,以免后续重复遍历该节点
                iterator.remove();
            }
        }
        // 返回按条件查询到的父节点
        return this;
    }

    public TreeUtils setChildren(LC leafCondition) {
        if (CollectionUtils.isEmpty(rootList)) {
            return this;
        } else {
            rootList.forEach(r -> {
                fillLeaves(r, leafCondition);
            });
            return this;
        }
    }

    public TreeUtils setChildren() {
        if (CollectionUtils.isEmpty(rootList)) {
            return this;
        } else {
            rootList.forEach(r -> {
                fillLeaves(r);
            });
            return this;
        }
    }

    /**
     * 给父节点填充叶子结点
     *
     * @param parent 父节点
     */
    public void fillLeaves(N parent) {
        List<N> children = new ArrayList<>();
        for (Iterator<N> ite = nodeList.iterator(); ite.hasNext(); ) {
            N node = ite.next();
            // 找出与当前父节点关联的叶子结点
            if (Objects.equals(node.getParentId(), parent.getTreeNodeId())) {
                node.setLevel(parent.getLevel() + 1);
                node.setPath(parent.getPath() + node.getId()+"/");
                children.add(node);
                // 从所有节点列表中删除该节点,以免后续重复遍历该节点
                ite.remove();
            }
        }
        // 继续递归叶子结点的遍历子节点
        children.forEach(m -> {
            // 递归设置子节点
            fillLeaves(m);
        });
        // 如果孩子为空,则直接返回,否则继续递归设置孩子的孩子
        if (children.isEmpty()) {
            return;
        }
        parent.setChildren(children);
    }

    /**
     * 按照自定义的规则,给父节点填充叶子结点
     *
     * @param parent        父节点
     * @param leafConfition 叶子结点的自定义判定规则的参数类型
     */
    public void fillLeaves(N parent, LC leafConfition) {
        List<N> children = new ArrayList<>();
        Object parentId = parent.getTreeNodeId();
        for (Iterator<N> ite = nodeList.iterator(); ite.hasNext(); ) {
            N node = ite.next();
            // 按自定义条件筛选子节点,null则表示没有自定义条件
            if (Objects.isNull(leafConfition) || node.isChildren(leafConfition)) {
                // 找出与当前父节点关联的叶子结点
                if (Objects.equals(node.getParentId(), parentId)) {
                    node.setLevel(parent.getLevel() + 1);
                    children.add(node);
                    // 从所有节点列表中删除该节点,以免后续重复遍历该节点
                    ite.remove();
                }
            }
        }
        // 如果孩子为空,则直接返回,否则继续递归设置孩子的孩子
        if (children.isEmpty()) {
            return;
        }
        // 继续递归叶子结点的遍历子节点
        children.forEach(m -> {
            // 递归设置子节点
            fillLeaves(m, leafConfition);
        });
        parent.setChildren(children);
    }

    /**
     * 根据结点id获取特定结点的子树,通过isParent来判断是否需要父节点
     *
     * @param rootId 结点id
     * @param <T>    结点id的对象类型
     * @return
     */
    public <T> TreeUtils getTreeByNodeId(T rootId) {
        for (Iterator<N> iterator = nodeList.iterator(); iterator.hasNext(); ) {
            N node = iterator.next();
            if (rootId.equals(node.getTreeNodeId())) {
                node.setLevel(0);
                rootList.add(node);
                iterator.remove();
            }
        }
        // 填充叶子结点
        rootList.forEach(root -> fillLeaves(root));
        return this;
    }


    /**
     * 遍历树型结构,并且根据回调函数执行相应的操作处理
     *
     * @param handle 回调函数
     * @param <K>    结果集的key
     * @param <V>    结果集的value
     * @return 返回一个结果集的map
     */
    public static <N extends TreeNode, K, V> Map<K, V> traverseTree(List<N> tree, FunctionHandle<N, K, V> handle) {
        Map<K, V> resultMap = new HashMap<>();
        for (Iterator<N> iterator = tree.iterator(); iterator.hasNext(); ) {
            N node = iterator.next();
            // 通过回调函数处理当前点
            if (handle != null) {
                handle.callback(node, resultMap);
            }
            if (node.hasChild()) {
                recursiveTree(node.getChildren(), resultMap, handle);
            }
        }
        return resultMap;
    }


    /**
     * 递归遍历子树,获取相应的处理结果
     *
     * @param children  子树集合
     * @param resultMap 结果集
     * @param handle    回调函数
     * @param <E>       集合类型
     */
    private static <E extends TreeNode> void recursiveTree(List<E> children, Map resultMap, FunctionHandle handle) {
        for (Iterator<E> iterator = children.iterator(); iterator.hasNext(); ) {
            E child = iterator.next();
            if (handle != null) {
                handle.callback(child, resultMap);
            }
            if (child.hasChild()) {
                recursiveTree(child.getChildren(), resultMap, handle);
            }
        }
    }
}





package org.example.tree;

import java.util.List;

/**
 * @ClassName TreeNode
 * @Description TODO
 * @Author hrp
 * @Date 2023/11/23 14:41
 */
public interface TreeNode<T, RC, LC> {

    /**
     * 获取树结点id
     * @return
     */
    T getTreeNodeId();

    /**
     * 获取该节点的父节点id
     * @return
     */
    T getParentId();

    /**
     * 判断该节点是否为根节点
     * @Des 可以用于简单树的组件
     * @return
     */
    boolean isRoot();

    /**
     * 自定义父结点的判定规则
     * @param rootCondition
     * @return
     */
    boolean isRoot(RC rootCondition);

    /**
     * 自定义子节点(叶子结点)的判定规则
     * @param leafCondition
     * @return
     */
    boolean isChildren(LC leafCondition);

    /**
     * 判断是否有子节点
     * @return
     */
    boolean hasChild();

    /**
     * 设置结点的子节点列表
     * @param children
     */
    void setChildren(List<? extends TreeNode<T, RC, LC>> children);

    /**
     * 获取所有子节点
     * @return
     */
    List<? extends TreeNode<T, RC, LC>> getChildren();

    /**
     * 获取树的深度
     * @return
     */
    Integer getLevel();

    /**
     * 设置树的深度
     */
    void setLevel(Integer level);

    /**
     * 设置树的路径
     */
    Integer getId();

    /**
     * 设置树的深度
     */
    void setId(Integer id);

    /**
     * 设置树的路径
     */
    String getPath();

    /**
     * 设置树的深度
     */
    void setPath(String path);

}



package org.example.tree;

import java.util.Map;

/**
 * @ClassName FunctionHandle
 * @Description TODO
 * @Author hrp
 * @Date 2023/11/23 14:42
 */
@FunctionalInterface
public interface FunctionHandle <N, K, V> {
    void callback(N node, Map<K,V> result);
}




package org.example.tree;

import lombok.Data;

import java.util.List;
import java.util.Objects;

/**
 * @ClassName MenuVo
 * @Description TODO
 * @Author hrp
 * @Date 2023/11/23 14:43
 */
@Data
public class MenuVo implements TreeNode<Integer, Boolean, Integer> {

    private Integer id;

    private String name;

    private Integer parentId;

    private Integer Level;

    private String path;

    private List<MenuVo> children;



    public MenuVo(Integer id, String name, Integer parentId) {
        this.id = id;
        this.name = name;
        this.parentId = parentId;
    }


    @Override
    public Integer getTreeNodeId() {
        return this.id;
    }

    @Override
    public Integer getParentId() {
        return this.parentId;
    }

    @Override
    public boolean isRoot() {
        return Objects.equals(this.parentId, 0);
    }


    @Override
    public boolean isRoot(Boolean rootCondition) {
        if (rootCondition){
//            Objects.nonNull();
            return Objects.equals(this.id, 14);

        } else {
            // 都不符合就走默认判定条件
            return isRoot();
        }
    }

    @Override
    public boolean isChildren(Integer leafCondition) {
        // 这里自定义规则当传入的参数等于1的时候 ——> 要销售部,只要销售部的A部门的子树节点
        if (leafCondition.equals(1)){
            if (this.name.contains("a")){
                return true;
            } else {
                return false;
            }
        } else {
            // 都不符合就表示该节点不是自定义规则中要的结点
            return false;
        }

    }

    @Override
    public boolean hasChild() {
        return !Objects.isNull(this.children);
    }

    @Override
    public void setChildren(List children) {
        this.children = children;
    }

    @Override
    public List<MenuVo> getChildren(){
        return this.children;
    }



}




@Test
public void testtree2(){
    //模拟从数据库查询出来
    List<MenuVo> menusVoList = new ArrayList<>(Arrays.asList(
            new MenuVo(1, "A公司", 0),
            new MenuVo(2, "a销售部", 14),
            new MenuVo(3, "财税部", 1),
            new MenuVo(4, "商务部", 1),
            new MenuVo(5, "综合部", 1),
            new MenuVo(6, "a销售1部", 2),
            new MenuVo(7, "a销售2部", 2),
            new MenuVo(8, "a销售3部", 2),
            new MenuVo(9, "a销售4部", 2),
            new MenuVo(10, "b销售部", 14),
            new MenuVo(11, "b销售1部", 10),
            new MenuVo(12, "b销售2部", 10),
            new MenuVo(13, "人事部", 1),
            new MenuVo(14, "销售部", 1)));

    List simpleTree = new TreeUtils<>(menusVoList).generateTrees().getTree();
    System.out.println(JSON.toJSON(simpleTree));

}