又更新了一个list转树状结构的工具类

之前写的TreeMenuUtil,之前用反射写的,这次稍微换了换,总的来说还是换汤不换药吧

1. TreeUtil

内部判断是否属于root节点使用的是stream().filter()

1. 工具类

java 复制代码
import cn.hutool.core.collection.CollUtil;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;

import java.util.List;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;

/**
 * @author eleven
 * @date 2025/1/7 14:47
 * @apiNote 工具类用于将包含父子关系信息的扁平列表转换为树形结构
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
@Accessors(chain = true)
public class TreeUtil<T> {
    /**
     * 要转化成树状结构的集合
     */
    private List<T> list;

    /**
     * 用于从T类型对象中提取标识节点自身的id的函数,
     */
    private Function<T, ?> idGetter;
    /**
     * 用于从T类型对象中提取标识父节点的parentId的函数
     */
    private Function<T, ?> parentIdGetter;
    /**
     * 用于筛选根节点的条件谓词
     */
    private Predicate<? super T> rootNodeFilter;
    /**
     * 用于设置子节点的方法名
     */
    private String childrenSetter;


    /**
     * 将扁平列表转换为树形结构的主方法
     *
     * @return 转换后的树形结构的根节点列表
     */
    public List<T> list2Tree() {
        // 先找出所有的根节点
        List<T> rootNode = rootNode(list, rootNodeFilter);
        list.removeAll(rootNode);
        // 遍历根节点,为每个根节点设置子节点,构建完整树形结构
        rootNode.forEach(root -> setChildren(root, list));
        return rootNode;
    }

    private void setChildren(T root, List<T> list) {
        List<T> children = list.stream()
                .filter(node -> parentIdGetter.apply(node).equals(idGetter.apply(root)))
                .collect(Collectors.toList());
        if (CollUtil.isNotEmpty(children)) {
            list.removeAll(children);
            try {
                root.getClass()
                        .getMethod(childrenSetter, List.class)
                        .invoke(root, children);
            } catch (Exception e) {
                e.printStackTrace();
            }
            children.forEach(child -> setChildren(child, list));
        }

    }

    /**
     * 根据条件筛选出根节点列表
     *
     * @param list      包含所有节点的扁平列表
     * @param predicate 用于筛选根节点的条件谓词
     * @return 根节点列表
     */
    private static <T> List<T> rootNode(List<T> list, Predicate<? super T> predicate) {
        return list.stream()
                .filter(predicate)
                .collect(Collectors.toList());
    }
}

2. 使用

java 复制代码
List<CollectCategory> list = categoryMapper.list(category);
return new TreeUtil<CollectCategory>()
        // 需要转换的集合
        .setList(list)
        // 获取id的方法
        .setIdGetter(CollectCategory::getId)
        // 获取parentId的方法
        .setParentIdGetter(CollectCategory::getParentId)
        // 筛选顶层节点的filter
        .setRootNodeFilter(item -> StrUtil.equalsIgnoreCase("0", item.getParentId()))
        // 设置chidren属性的方法名
        .setChildrenSetter("setChildren")
        // 转换方法
        .list2Tree();

2. TreeMenuUtil

第一次使用的时候按照parentId直接分组,不用每次都遍历list了,使用的是stream().collect(Collectors.groupingBy)

1. 工具类

复制代码
import cn.hutool.core.collection.CollUtil;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;

/**
 * @author eleven
 * @date 2025/1/7 16:50
 * @apiNote
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
@Accessors(chain = true)
public class TreeMenuUtil<T> {
    /**
     * 要转化成树状结构的集合
     */
    private List<T> list;

    /**
     * 用于从T类型对象中提取标识节点自身的id的函数,
     */
    private Function<T, ?> idGetter;
    /**
     * 用于从T类型对象中提取标识父节点的parentId的函数
     */
    private Function<T, ?> parentIdGetter;
    /**
     * 用于筛选根节点的条件谓词
     */
    private Predicate<? super T> rootNodeFilter;
    /**
     * 用于设置子节点的方法名
     */
    private String childrenSetter;

    private Object rootParentId;

    public List<T> list2Tree() {
        List<T> rootNodes = new ArrayList<>();
        if (rootParentId == null) {
            rootNodes = list.stream()
                    .filter(item -> Objects.isNull(parentIdGetter.apply(item)))
                    .collect(Collectors.toList());
            list.removeAll(rootNodes);
        }
        Map<?, List<T>> listMap = list.stream()
                .collect(Collectors.groupingBy(parentIdGetter));
        if (CollUtil.isEmpty(rootNodes)) {
            rootNodes = listMap.get(rootParentId);
        }
        
        if (CollUtil.isEmpty(rootNodes)) {
            // 自行处理一下没有根节点的情况,这里直接返回原数组了
            return list;
        }

        rootNodes.forEach(root -> setChildren(root, listMap));
        return rootNodes;
    }

    /**
     * @param parent  根节点
     * @param listMap 包含所有节点的Map
     */
    private void setChildren(T parent, Map<?, List<T>> listMap) {
        Object parentId = idGetter.apply(parent);
        List<T> children = listMap.get(parentId);
        if (CollUtil.isNotEmpty(children)) {
            try {
                parent.getClass()
                        .getMethod(childrenSetter, List.class)
                        .invoke(parent, children);
            } catch (Exception e) {
                e.printStackTrace();
            }
            children.forEach(child -> setChildren(child, listMap));
        }
    }
}

2. 使用

复制代码
return new TreeMenuUtil<CollectCategory>()
        // 要处理的list
        .setList(list)
        // 获取id的Function
        .setIdGetter(CollectCategory::getId)
        // 获取parentId的Function
        .setParentIdGetter(CollectCategory::getParentId)
        // 根节点的parentId, 可以为null
        .setRootParentId(0)
        // 设置子节点的方法名
        .setChildrenSetter("setChildren")
        // 筛选根节点的条件
        .list2Tree();

3. 用@Builder改造了一下,使用起来好像更简单了点

1. @Builder标注

java 复制代码
import cn.hutool.core.collection.CollUtil;
import lombok.Builder;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;

/**
 * @author eleven
 * @date 2025/1/7 16:50
 * @apiNote
 */
@Builder
public class TreeMenuUtil<T> {
    /**
     * 要转化成树状结构的集合
     */
    private List<T> list;

    /**
     * 用于从T类型对象中提取标识节点自身的id的函数,
     */
    private Function<T, ?> idGetter;
    /**
     * 用于从T类型对象中提取标识父节点的parentId的函数
     */
    private Function<T, ?> parentIdGetter;
    /**
     * 用于筛选根节点的条件谓词
     */
    private Predicate<? super T> rootNodeFilter;
    /**
     * 用于设置子节点的方法名
     */
    private String childrenSetter;

    private Object rootParentId;

    public List<T> list2Tree() {
        List<T> rootNodes = new ArrayList<>();
        if (rootParentId == null) {
            rootNodes = list.stream()
                    .filter(item -> Objects.isNull(parentIdGetter.apply(item)))
                    .collect(Collectors.toList());
        }
        Map<?, List<T>> listMap = list.stream()
                .filter(item -> !Objects.isNull(parentIdGetter.apply(item)))
                .collect(Collectors.groupingBy(parentIdGetter));
        if (CollUtil.isEmpty(rootNodes)) {
            rootNodes = listMap.get(rootParentId);
        }
        if (CollUtil.isEmpty(rootNodes)) {
            // 自行处理一下没有根节点的情况,这里直接返回原数组了
            return list;
        }
        rootNodes.forEach(root -> setChildren(root, listMap));
        return rootNodes;
    }

    /**
     * @param parent  根节点
     * @param listMap 包含所有节点的Map
     */
    private void setChildren(T parent, Map<?, List<T>> listMap) {
        Object parentId = idGetter.apply(parent);
        List<T> children = listMap.get(parentId);
        if (CollUtil.isNotEmpty(children)) {
            try {
                parent.getClass()
                        .getMethod(childrenSetter, List.class)
                        .invoke(parent, children);
            } catch (Exception e) {
                e.printStackTrace();
            }
            children.forEach(child -> setChildren(child, listMap));
        }
    }
}

2. 使用

说实话,这个builder的泛型是这么加的我是真没想到

java 复制代码
return TreeMenuUtil.<CollectCategory>builder()
        .list(list)
        .idGetter(CollectCategory::getId)
        .parentIdGetter(CollectCategory::getParentId)
        .rootParentId(category.getRootParentId())
        .childrenSetter("setChildren")
        .build()
        .list2Tree();
相关推荐
en.en..1 小时前
数据结构考点解析:逻辑闭环与存储循环辨析
数据结构
笨鸟先飞,勤能补拙3 小时前
从预测到决策:人工智能与机器学习的系统方法、工程闭环与现实边界
大数据·人工智能·windows·python·机器学习·密码学
布莱克6054 小时前
数据库索引分类:数据结构、物理存储与逻辑角度详解
数据结构·数据库
程序员-李俞4 小时前
Mistral OCR 4真正改变的不是“识字”:文档AI正在变成Agent的数据入口
人工智能·windows·ai作画·aigc·ocr·ai编程·ai写作
Fa_Mian_Tuan4 小时前
图论基础|邻接矩阵超详细讲解(含无向/有向/带权图+完整可运行C语言代码)
c语言·数据结构·笔记·算法·图论
C++ 老炮儿的技术栈5 小时前
Qt5.9.1 Windows 完整开发环境搭建流程
开发语言·c++·windows·qt·编辑器·代码化
笨鸟先飞,勤能补拙6 小时前
AI与深度学习:从原理到应用
人工智能·windows·vscode·深度学习·机器学习·网络安全·github
bkspiderx6 小时前
从零开始:VS Code搭建C/C++开发环境全指南(Windows/macOS/Linux)
c语言·c++·windows·vs code·c/c++开发环境
小星星闪亮登场6 小时前
ST表--倍增思想
开发语言·数据结构·c++·算法·思维
Rudon滨海渔村7 小时前
镜像转换esd转iso - VMware虚拟机如何安装或恢复(.esd)的Windows系统
windows·vmware·iso·esd