多叉数API 提供树化方法

143 阅读2分钟

适用于需要多叉树业务场景,例如模拟文件夹等

package com.daicy.compowercloud.api;

import com.alibaba.fastjson.annotation.JSONField;
import com.daicy.compowercloud.workfile.model.FileTreeEntity;
import com.google.common.collect.Lists;
import lombok.Data;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @author : Duanshaojie
 * @ClassName: HashHashTreeNode
 * @date 2022-09-13-10:20
 * @description :
 */
public class TreeNode<T extends TreeNode.NodeData> {

    /**
     * 节点数据
     */
    private T data;
    /**
     * 子节点
     */
    private List<TreeNode<T>> sonNodes = new LinkedList();


    /**
     * 当前节点放置子节点
     *
     * @param data 数据
     * @return com.dsj.api.HashTreeNode
     * @author duanshaojie
     * @date 2022/09/13 10:52
     */
    public void putSon(T data) {
        TreeNode<T> sonNode = new TreeNode<T>();
        sonNode.setData(data);
        sonNodes.add(sonNode);
    }

    /**
     * 当前节点放置子节点
     *
     * @return com.dsj.api.HashTreeNode
     * @author duanshaojie
     * @date 2022/09/13 10:52
     */
    public void putSon(TreeNode<T> sonNode) {
        sonNodes.add(sonNode);
    }

    /**
     * 当前节点是否叶子节点
     *
     * @return boolean
     * @author duanshaojie
     * @date 2022/09/13 10:53
     */
    public boolean isLeafNode() {
        return sonNodes.size() == 0;
    }

    /**
     * 获取子节点集合
     *
     * @return java.util.List<com.dsj.api.HashTreeNode < T>>
     * @author duanshaojie
     * @date 2022/09/13 10:53
     */
    public List<TreeNode<T>> getSonNodeList() {
        return this.sonNodes;
    }


    /**
     * 获取所有指定节点的子节点(递归调用)
     *
     * @param hashTreeNode
     * @return java.util.List<com.dsj.api.HashTreeNode < T>>
     * @author duanshaojie
     * @date 2022/09/13 11:06
     */
    private List<TreeNode<T>> getAllSonNode(TreeNode<T> hashTreeNode) {
        if (Objects.isNull(hashTreeNode)) {
            return Collections.emptyList();
        }
        List<TreeNode<T>> dataList = new ArrayList<>();
        dataList.addAll(sonNodes);
        for (TreeNode<T> sonNode : hashTreeNode.getSonNodeList()) {
            dataList.addAll(getAllSonNode(sonNode));
        }
        return dataList;
    }

    /**
     * 获取当前节点数据
     *
     * @return T
     * @author duanshaojie
     * @date 2022/09/13 11:07
     */
    public T getData() {
        return this.data;
    }

    /**
     * 设置当前节点数据
     *
     * @param data
     * @return void
     * @author duanshaojie
     * @date 2022/09/13 11:07
     */
    public void setData(T data) {
        this.data = data;
    }

    /**
     * 树化方法
     *
     * @param dataList
     * @return
     */
    public TreeNode<T> getTree(List<T> dataList) {
        //节点id和节点的映射
        Map<String, TreeNode<T>> treeEntityMap = dataList.stream().collect(Collectors.toMap(entity -> entity.getId().toString()
                , entity -> {
                    TreeNode node = new TreeNode<>();
                    node.setData(entity);
                    return node;
                }));
        //根节点
        TreeNode<T> rootNode = null;
        /**
         *  倒着遍历所有数据 保证现将叶子节点放至父节点下后再归位父节点 思路参考归并排序
         */
        for (int i = dataList.size() - 1; i >= 0; i--) {
            //当前节点
            TreeNode<T> thisNode = treeEntityMap.get(dataList.get(i).getId().toString());
            //父节点
            TreeNode<T> parentNode = treeEntityMap.get(thisNode.getData().getParentId().toString());
            /**
             * 如果当前节点的父节点在映射表中 则在父节点中添加此节点
             */
            if (Objects.nonNull(parentNode)) {
                parentNode.putSon(thisNode);
            }
            /**
             * 判断根节点
             */
            if (thisNode.getData().getParentId() == 0) {
                rootNode = thisNode;
            }
        }
        //返回Map的最前面的节点
        return rootNode;
    }

    public static class NodeData {
        Integer id, parentId;

        public Integer getId() {
            return id;
        }

        public void setId(int id) {
            this.id = id;
        }

        public Integer getParentId() {
            return parentId;
        }

        public void setParentId(int parentId) {
            this.parentId = parentId;
        }
    }
}

使用方法 数据继承TreeNode.NodeData 树化时将数据集合传入getTree()方法 内部会根据id和parentId树化 如需指定根节点请修改根节点判断条件 代码中默认根节点的父节点为0

附带sql
CREATE TABLE file_tree ( id int(11) NOT NULL COMMENT '主键id', node_name varchar(255) DEFAULT NULL COMMENT '节点名称(用户根节点为用户id)', parent_id int(11) DEFAULT NULL COMMENT '父id 跟目录为0', node_type int(11) DEFAULT NULL COMMENT '节点类型:1-文件夹;2-文件;', bucket_name varchar(60) DEFAULT NULL COMMENT '数据桶名称(文件类型必传)', file_key varchar(255) DEFAULT NULL COMMENT '文件Key COS文件key', res_platform int(11) DEFAULT NULL COMMENT '源平台:1-腾讯;', user_id varchar(60) DEFAULT NULL COMMENT '用户id ', PRIMARY KEY (id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;