树型结构的通用实现方法

本文介绍了一种树型结构的通用实现方法,通过将对象的parentId作为key存储在parentMap中,从顶级节点开始迭代获取下级节点。实现过程中需要实现Node接口,提供了具体的代码示例和IDEA编辑的demo项目。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

树型结构的通用实现方法

  1. 树型结构的通用实现demo 下载0积分
  2. 实现思路 -> 将每个对象以其parentId作为key,填充到parentMap中,然后寻找顶级节点,然后迭代每一个顶级节点,以每一个顶级节点的id作为key值从parentMap中获取下级节点
  3. Node 接口 如果需要使用此通用实现方法 需要实现Node接口
public interface Node {

    String getId();

    void setId(String id);

    String getParentId();

    void setParentId(String parentId);
}
  1. 具体的实现代码
/**
 * 树型结构的实现组件
 *
 * @param <T> 需要实现Node接口
 */
public interface TreeImplComponent<T extends Node> {

    /**
     * 默认填充parentMap实现方法
     * Map<parentId, Map<id, T>>
     *
     * @param list      list
     * @param parentMap parentMap
     */
    default void fillParentMap(List<T> list, Map<String, Map<String, T>> parentMap) {
        this.fillParentMap(list, parentMap, t -> {
            // nothing to do
        });
    }

    /**
     * 默认填充parentMap实现方法
     * Map<parentId, Map<id, T>>
     *
     * @param list      list
     * @param parentMap parentMap
     * @param consumer  consumer
     */
    default void fillParentMap(List<T> list, Map<String, Map<String, T>> parentMap, Consumer<T> consumer) {
        list.forEach(t -> {
            String parentId = t.getParentId();
            this.putData(parentMap, parentId, t);
            // 比如id是字符串 需要使用其他字段来确定顶级节点
            consumer.accept(t);
        });
    }

    /**
     * 填充基础的数据 比如parentMap 比如topNodes
     * map结构如下:Map<parentId, Map<id, T>>
     *
     * @param list      list
     * @param parentMap parentMap
     * @param topNodes  topNodes
     */
    void fillBasicData(List<T> list, Map<String, Map<String, T>> parentMap, List<T> topNodes);

    /**
     * 排序
     * 每一级的节点顺序 比如A节点下有B、C两个节点
     * 当迭代到A节点时,其下的B、C节点会使用这个方法进行排序,根据排序结果决定B、C的顺序,然后迭代B、C生成树
     *
     * @param list list
     * @return sorted list
     */
    List<T> sort(List<T> list);

    /**
     * 生成一个新的实例 并复制旧实例的属性
     * 不建议使用Java Bean复制,建议使用get set 效率更快
     *
     * @param ori ori
     * @return T
     */
    T copyProperties(T ori);

    /**
     * 生成树型结构
     *
     * @param list     list
     * @param consumer consumer 设置生成好的children到T实例的对应属性
     * @return tree list
     */
    default List<T> buildTree(List<T> list, BiConsumer<T, List<T>> consumer) {
        if (CollectionUtils.isNotEmpty(list)) {
            if (list.size() > 1) {
                Map<String, Map<String, T>> parentMap = this.synchronizedMap();
                List<T> topNodes = this.synchronizedList(100);
                // 填充parentMap: Map<parentId, Map<id, T>> 以及获取topNodes
                this.fillBasicData(list, parentMap, topNodes);
                // 生成树形结构
                return this.buildTree(topNodes, parentMap, consumer);
            } else {
                return list;
            }
        } else {
            return this.emptyList();
        }
    }

    /**
     * 生成树的默认实现
     * 将顶级节点展开 迭代每一个顶级节点
     *
     * @param topNodes  topNodes 顶级节点集合
     * @param parentMap parentMap map结构如下:Map<parentId, Map<id, T>>
     * @param consumer  consumer 设置 children属性的函数
     * @return tree list
     */
    default List<T> buildTree(List<T> topNodes, Map<String, Map<String, T>> parentMap, BiConsumer<T, List<T>> consumer) {
        List<T> nodes = this.synchronizedList(100);
        // 先进行一次排序
        this.sort(topNodes).forEach(t -> {
            if (t != null) {
                T node = this.copyProperties(t);
                String id = t.getId();
                // 根据id获取下级节点
                Map<String, T> map = parentMap.get(id);
                List<T> children;
                if (map != null) {
                    // 递归 构建树
                    children = this.buildTree(this.synchronizedList(map.values()), parentMap, consumer);
                } else {
                    // 设置一个空的children list 并非为null
                    children = this.emptyList();
                }
                // 设置children的值
                consumer.accept(node, children);
                nodes.add(node);
            }
        });
        return nodes;
    }

    /**
     * 默认完全展开树型结构的实现方法
     *
     * @param nodes    nodes
     * @param function function 获取children值的函数
     */
    default List<T> layoutTree(List<T> nodes, Function<T, List<T>> function) {
        List<T> list = this.synchronizedList(1000);
        // 完全展开树型结构
        this.layoutTree(nodes, list, function);
        return list;
    }

    /**
     * 默认完全展开树型结构的实现方法
     *
     * @param nodes    nodes
     * @param results  results
     * @param function function
     */
    default void layoutTree(List<T> nodes, List<T> results, Function<T, List<T>> function) {
        // 比如A节点下有B、C两个节点 最终完全展开的树型结构的顺序时A -> B -> C
        nodes.forEach(node -> {
            // 复制一条新的数据
            T dest = this.copyProperties(node);
            // 通过function获取children
            List<T> children = function.apply(dest);
            // 先添加当前tree
            // 后添加其下级tree
            results.add(dest);
            if (CollectionUtils.isNotEmpty(children)) {
                this.layoutTree(children, results, function);
            }
        });
    }

    default <K, V> Map<K, V> synchronizedMap() {
        return Collections.synchronizedMap(new LinkedHashMap<>());
    }

    default <K> List<K> synchronizedList(int initialCapacity) {
        return this.synchronizedList(new ArrayList<>(initialCapacity));
    }

    default <K> List<K> synchronizedList(Collection<K> coll) {
        return Collections.synchronizedList(new ArrayList<>(coll));
    }

    default <K> List<K> emptyList() {
        return Collections.emptyList();
    }

    default <K, V extends Node> void putData(Map<K, Map<String, V>> dataMap, K key, V v) {
        Map<String, V> map = dataMap.get(key);
        Map<String, V> tempMap = this.synchronizedMap();
        if (map != null) {
            map.forEach(tempMap::put);
        }
        tempMap.put(v.getId(), v);
        dataMap.put(key, tempMap);
    }
}
  1. 附带的demo项目 使用idea编辑的
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值