在大批量数据组装树结构时,使用递归组装会影响性能,并且排查问题非常麻烦。
这里把递归组装树结构改为迭代的方式,并且该工具类适用所有场景,包括树结构排序。
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));
}
}