又更新了一个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();
相关推荐
满天星830357718 小时前
【C++】特殊类设计
c++·windows
白驹过隙^^19 小时前
windows通过docker compose部署oktopus服务
linux·windows·tcp/ip·docker·容器·开源
Frank_refuel19 小时前
C++之内存管理
java·数据结构·c++
菜鸟233号19 小时前
力扣343 整数拆分 java实现
java·数据结构·算法·leetcode
yuanmenghao19 小时前
自动驾驶中间件iceoryx - 内存与 Chunk 管理(三)
数据结构·c++·算法·链表·中间件·自动驾驶
小宇的天下19 小时前
innovus Flip chip 产品设计方法(3)
数据库·windows·microsoft
茶猫_20 小时前
C++学习记录-旧题新做-链表求和
数据结构·c++·学习·算法·leetcode·链表
广州服务器托管20 小时前
[2026.1.6]WINPE运维版20260106,带网络功能的PE维护系统
运维·开发语言·windows·计算机网络·个人开发·可信计算技术
yuniko-n20 小时前
【牛客面试 TOP 101】链表篇(一)
数据结构·算法·链表·面试·职场和发展
王老师青少年编程20 小时前
信奥赛C++提高组csp-s之并查集(案例实践)1
数据结构·c++·并查集·csp·信奥赛·csp-s·提高组