树结构处理,list和tree互转

1、实体类

java 复制代码
package com.iot.common.test.entity;

import lombok.Data;

import java.util.List;

/**
 * @description:
 * @author:zilong
 * @date:2023/9/8
 */
@Data
public class Node {
    //id
    private String id;
    //父节点id
    private String pId;
    //名称
    private String name;
    //编码
    private String code;
    //层级
    private String level;
    private List<Node> children;

    public Node(String id, String pId, String name, String code, String level) {
        this.id = id;
        this.pId = pId;
        this.name = name;
        this.code = code;
        this.level = level;
    }


    public Node() {

    }
}

2、工具类

java 复制代码
package com.iot.common.util;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
import com.iot.common.test.entity.Node;
import lombok.SneakyThrows;

import java.lang.invoke.SerializedLambda;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.BiConsumer;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 树工具类
 */

public class TreeUtil {


    /**
     * 基础数据转树结构Map版本(速度比递归要快很多)
     *
     * @param sourceList  需转换的数据
     * @param getId       主键
     * @param getParentId 父id (父id必须和主键相同类型)
     * @param getChildren 子集
     * @param setChildren 子集
     * @return tree
     */
    public static <T, R> List<T> listToTree(List<T> sourceList, Function<T, R> getId, Function<T, R> getParentId,
                                            Function<T, List<T>> getChildren, BiConsumer<T, List<T>> setChildren) {

        Map<R, T> oldMap = sourceList.stream().collect(Collectors.toMap(getId, T -> T));
        List<T> treeList = new ArrayList<>();
        sourceList.forEach(tree -> {
            T parent = oldMap.get(getParentId.apply(tree));
            if (parent == null) {
                treeList.add(tree);
            } else {
                List<T> ch = getChildren.apply(parent);
                if (ch == null) {
                    ch = new ArrayList<>();
                }
                ch.add(tree);
                setChildren.accept(parent, ch);
            }
        });
        return treeList;
    }

    /**
     * 树结构转父子排序列表且按父在前,子在后,进行排序。
     *
     * @param treeList    父子树列表(带有id、parentId和children的模型列表)
     * @param getId
     * @param getParentId
     * @param getChildren
     * @param setChildren
     * @param <T>
     * @param <R>
     * @return
     */
    public static <T, R> List<T> treeListToSortList(List<T> treeList, Function<T, R> getId, Function<T, R> getParentId,
                                                    Function<T, List<T>> getChildren, BiConsumer<T, List<T>> setChildren) {
        // 先整成树形结构
        List<T> list = listToTree(treeList, getId, getParentId, getChildren, setChildren);
        List<T> sortList = new ArrayList<>();
        tree2List(sortList, list.get(0), getChildren, setChildren);
        return sortList;
    }

    /**
     * 树结构转列表
     *
     * @param result      结果容器
     * @param t           树顶部元素
     * @param getChildren
     * @param <T>
     */
    private static <T> void tree2List(List<T> result, T t, Function<T, List<T>> getChildren, BiConsumer<T, List<T>> setChildren) {
        //根据条件判断是否需要添加至列表
        result.add(t);
        List<T> children = getChildren.apply(t);
        // 将children置成空
        setChildren.accept(t, null);
        //没有子级
        if (children == null || children.size() == 0) {
            return;
        }
        //存在子级,递归调用
        for (T child : children) {
            tree2List(result, child, getChildren, setChildren);
        }
    }

    /**
     * 基础数据集转树结构
     *
     * @param baseList      树结构的基础数据集
     * @param getIdFn       获取主键的函数
     * @param getParentIdFn 获取父节点的函数
     * @param getChildrenFn 获取子集的函数
     * @param <T>           t
     * @param <R>           r
     * @return t
     */
    @SneakyThrows
    public static <T, R> List<T> treeOut(List<T> baseList, Function<T, R> getIdFn, Function<T, R> getParentIdFn, SFunction<T, R> getChildrenFn) {
        /*所有元素的Id*/
        List<Object> ids = baseList.stream().map(getIdFn).collect(Collectors.toList());
        /*查出所有顶级节点*/
        List<T> topLevel = baseList.stream().filter(x -> {
            R apply = getParentIdFn.apply(x);
            return !ids.contains(apply);
        }).collect(Collectors.toList());
        return TreeUtil.recursion(topLevel, baseList, getIdFn, getParentIdFn, getChildrenFn);
    }

    /**
     * 指定顶级元素的基础数据集转树结构
     *
     * @param list
     * @param top
     * @param getIdFn
     * @param getParentIdFn
     * @param getChildrenFn
     * @param <T>
     * @param <R>
     * @return
     */
    @SneakyThrows
    public static <T, R> List<T> treeOutWithTop(List<T> list, T top, Function<T, R> getIdFn, Function<T, R> getParentIdFn, SFunction<T, R> getChildrenFn) {
        ArrayList<T> ts = new ArrayList<>();
        ts.add(top);
        return TreeUtil.recursion(ts, list, getIdFn, getParentIdFn, getChildrenFn);
    }

    @SneakyThrows
    private static <T, R> List<T> recursion(List<T> superLevel, List<T> list, Function<T, R> getIdFn, Function<T, R> getParentIdFn, SFunction<T, R> getChildrenFn) {
        //获取setChildren的Method
        Method writeReplaceMethod = getChildrenFn.getClass().getDeclaredMethod("writeReplace");
        boolean accessible = writeReplaceMethod.isAccessible();
        writeReplaceMethod.setAccessible(true);
        SerializedLambda serializedLambda = (SerializedLambda) writeReplaceMethod.invoke(getChildrenFn);
        writeReplaceMethod.setAccessible(accessible);
        String setMethodName = serializedLambda.getImplMethodName().replaceFirst("g", "s");
        Method setMethod = Class.forName(serializedLambda.getImplClass().replace("/", ".")).getDeclaredMethod(setMethodName, List.class);

        for (T t : superLevel) {
            List<T> children = list.stream().filter(x -> {
                R apply = getParentIdFn.apply(x);
                R apply1 = getIdFn.apply(t);
                return apply.equals(apply1);
            }).collect(Collectors.toList());
            if (children.size() <= 0) {
                continue;
            }

            List<T> recursion = recursion(children, list, getIdFn, getParentIdFn, getChildrenFn);
            setMethod.invoke(t, recursion);
        }
        return superLevel;
    }

    /**
     * 将列表转换为树形结构
     * 注:此方法的根节点的pid需要为null
     *
     * @param nodeList 列表
     * @return 树形结构
     */
    public static List<Node> buildTree(List<Node> nodeList) {
        // 存储所有节点
        Map<String, Node> nodeMap = new HashMap<>(nodeList.size());
        // 存储根节点
        List<Node> rootList = new ArrayList<>();

        // 将所有节点存储到map中,并找出根节点
        for (Node node : nodeList) {
            nodeMap.put(node.getId(), node);
            if (node.getPId() == null) {
                rootList.add(node);
            }
        }

        // 遍历所有节点,将子节点添加到父节点的children中
        for (Node node : nodeList) {
            String pId = node.getPId();
            if (pId != null) {
                Node parentNode = nodeMap.get(pId);
                if (parentNode != null) {
                    if (parentNode.getChildren() == null) {
                        parentNode.setChildren(new ArrayList<>());
                    }
                    parentNode.getChildren().add(node);
                }
            }
        }
        return rootList;
    }

    public static void main(String[] args) {
        ArrayList<Node> list = new ArrayList<Node>() {
            {
                add(new Node("1", "0", "A", "1", "1"));
                add(new Node("2", "0", "B", "2", "1"));
                add(new Node("3", "1", "A-1", "11", "2"));
                add(new Node("4", "1", "A-2", "12", "2"));
                add(new Node("5", "3", "A-1-1", "111", "3"));
                add(new Node("6", "5", "A-1-1-1", "1111", "4"));
                add(new Node("7", "6", "A-1-1-1-1", "11111", "5"));
                add(new Node("8", "2", "B-1", "21", "2"));
                add(new Node("9", "8", "B-1-1", "211", "3"));
                add(new Node("10", "9", "B-1-1-1", "2111", "4"));
            }
        };


        //使用工具类 treeOut:
        List<Node> result = TreeUtil.treeOut(list, Node::getId, Node::getPId, Node::getChildren);
        System.out.println("-----【treeOut】-----");
        System.out.println(JSON.toJSONString(result));

        //使用工具类 listToTree(Map):
        List<Node> result1 = TreeUtil.listToTree(list, Node::getId, Node::getPId, Node::getChildren, Node::setChildren);
        System.out.println("-----【listToTree】-----");
        System.out.println(JSON.toJSONString(result1));

        //使用工具类 treeOutWithTop:
        Node top = new Node();
        top.setId("3");
        List<Node> result2 = TreeUtil.treeOutWithTop(list, top, Node::getId, Node::getPId, Node::getChildren);
        System.out.println("-----【treeOutWithTop】-----");
        System.out.println(JSON.toJSONString(result2));

        //使用工具类 buildTree:
        List<Node> result3 = TreeUtil.buildTree(list);
        System.out.println("-----【buildTree】-----");
        System.out.println(JSON.toJSONString(result3));
    }


}
相关推荐
菜鸡中的奋斗鸡→挣扎鸡6 小时前
滑动窗口 + 算法复习
数据结构·算法
axxy20007 小时前
leetcode之hot100---240搜索二维矩阵II(C++)
数据结构·算法
Uu_05kkq8 小时前
【C语言1】C语言常见概念(总结复习篇)——库函数、ASCII码、转义字符
c语言·数据结构·算法
1nullptr10 小时前
三次翻转实现数组元素的旋转
数据结构
TT哇10 小时前
【数据结构练习题】链表与LinkedList
java·数据结构·链表
A懿轩A10 小时前
C/C++ 数据结构与算法【栈和队列】 栈+队列详细解析【日常学习,考研必备】带图+详细代码
c语言·数据结构·c++·学习·考研·算法·栈和队列
1 9 J12 小时前
数据结构 C/C++(实验五:图)
c语言·数据结构·c++·学习·算法
汝即来归12 小时前
选择排序和冒泡排序;MySQL架构
数据结构·算法·排序算法
aaasssdddd9615 小时前
C++的封装(十四):《设计模式》这本书
数据结构·c++·设计模式
芳菲菲其弥章15 小时前
数据结构经典算法总复习(下卷)
数据结构·算法