TreeUtils工具类 列表转树以及排序,适用于所有的列表

在大批量数据组装树结构时,使用递归组装会影响性能,并且排查问题非常麻烦。

这里把递归组装树结构改为迭代的方式,并且该工具类适用所有场景,包括树结构排序。

package com.lzk.demo.test.迭代代替递归;

import lombok.Data;

import java.util.ArrayList;
import java.util.List;

@Data
public class TreeNode {

    private Integer id;

    private Integer parentId;

    private List<TreeNode> children;

    public TreeNode(Integer id, Integer parentId) {
        this.id = id;
        this.parentId = parentId;
        this.children = new ArrayList<>();
    }

    public TreeNode() {
    }

}
package com.lzk.demo.test.迭代代替递归;

import com.alibaba.fastjson.JSONObject;

import java.lang.reflect.Field;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * @ClassName: Tree
 * @Author: lizhikang
 * @Description:
 * @CreateTime: 2024/01/05 15:32
 */

public class TreeUtils {

    /**
     * @param nodesList         数据
     * @param pid               父ID
     * @param idFieldName       ID字段名称
     * @param parentIdFieldName 父ID字段名称
     * @param childrenFieldName 子集字段名称
     * @param function          操作子集函数 通常可以做排序 如:stream -> stream.sorted(Comparator.comparing(TreeNode::getId, Comparator.reverseOrder())) 根据ID倒序
     *                          如果不想排序直接 stream -> stream
     * @param <T>
     * @param <R>
     * @return
     */
    public static <T, R> List<T> buildTree(List<T> nodesList, R pid, String idFieldName, String parentIdFieldName, String childrenFieldName, Function<Stream<T>, Stream<T>> function) {

        Map<Object, List<T>> map = nodesList.stream().collect(Collectors.groupingBy(a -> {
            try {
                Field field = a.getClass().getDeclaredField(parentIdFieldName);
                field.setAccessible(true);
                return field.get(a);
            } catch (NoSuchFieldException | IllegalAccessException e) {
                throw new RuntimeException("Error reading id field: " + e.getMessage());
            }
        }));

        List<T> roots = new ArrayList<>();

        // 逐层构建树结构
        roots.addAll(function.apply(Optional.ofNullable(map.get(pid)).orElse(new ArrayList<>()).stream()).collect(Collectors.toList()));
        Queue<T> queue = new LinkedList<>(roots);

        while (!queue.isEmpty()) {
            try {
                T node = queue.poll();
                Field idField = node.getClass().getDeclaredField(idFieldName);
                idField.setAccessible(true);
                Object nodeId = idField.get(node);

                if (map.containsKey(nodeId)) {
                    Field childrenField = node.getClass().getDeclaredField(childrenFieldName);
                    childrenField.setAccessible(true);
                    List<T> list = function.apply(Optional.ofNullable(map.get(nodeId)).orElse(new ArrayList<>()).stream()).collect(Collectors.toList());
                    childrenField.set(node, list);
                    queue.addAll(list);
                }

            } catch (NoSuchFieldException | IllegalAccessException e) {
                throw new RuntimeException("Error reading id field: " + e.getMessage());
            }
        }

        return roots;
    }

    public static void main(String[] args) throws IllegalAccessException, NoSuchFieldException {

        // 假设有一组节点
        List<TreeNode> nodes = new ArrayList<>();
        nodes.add(new TreeNode(1, 0));
        nodes.add(new TreeNode(2, 1));
        nodes.add(new TreeNode(3, 1));
        nodes.add(new TreeNode(4, 2));
        nodes.add(new TreeNode(5, 2));
        nodes.add(new TreeNode(6, 3));
        nodes.add(new TreeNode(11, 0));
        nodes.add(new TreeNode(12, 0));
        nodes.add(new TreeNode(13, 11));
        nodes.add(new TreeNode(14, 11));
        //不排序
        //List<TreeNode> tree = buildTree(nodes, 0, "id", "parentId", "children", stream -> stream);
        //排序 根据ID倒序
        List<TreeNode> tree = buildTree(nodes, 0, "id", "parentId", "children", stream -> stream.sorted(Comparator.comparing(TreeNode::getId, Comparator.reverseOrder())));

        System.out.println(JSONObject.toJSON(tree));
    }


}

  • 10
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值