陈昶聿
昨天 16b3634770a117c67433304d939ac3d5bd6a6ed4
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
package com.ruoyi.common.utils;
 
import com.ruoyi.common.core.domain.entity.SysDept;
 
import java.util.*;
import java.util.function.BiConsumer;
import java.util.function.Function;
import java.util.stream.Collectors;
 
/**
 * 树结构工具类:根据 parentId 构造树、一次性获取所有叶子节点。
 * 核心方法为泛型实现,id / parentId / children 通过函数注入,可复用于任何节点类型。
 */
public class TreeUtils {
 
    private TreeUtils() {
    }
 
    /* ================== 通用方法 ================== */
 
    /**
     * 根据 parentId 构造树,O(n) 一次遍历。
     * 根节点为「父Id不在集合中」的节点;若不存在根节点(如全是自引用的环)则原样返回。
     *
     * @param nodes          节点列表
     * @param idGetter       取节点id
     * @param parentIdGetter 取父节点id
     * @param childrenSetter 设置子节点列表
     */
    public static <T, K> List<T> buildTree(
            List<T> nodes,
            Function<T, K> idGetter,
            Function<T, K> parentIdGetter,
            BiConsumer<T, List<T>> childrenSetter) {
        if (nodes == null || nodes.isEmpty()) {
            return new ArrayList<>();
        }
        // 按父Id分组,一次遍历拿到每个节点的子列表
        Map<K, List<T>> byParent = nodes.stream().collect(Collectors.groupingBy(parentIdGetter));
        for (T node : nodes) {
            childrenSetter.accept(node, byParent.getOrDefault(idGetter.apply(node), new ArrayList<>()));
        }
        Set<K> ids = nodes.stream().map(idGetter).filter(Objects::nonNull).collect(Collectors.toSet());
        List<T> roots = nodes.stream()
                .filter(n -> !ids.contains(parentIdGetter.apply(n)))
                .collect(Collectors.toList());
        return roots.isEmpty() ? nodes : roots;
    }
 
    /**
     * 一次性获取所有叶子节点:即「id 不出现在任何节点的 parentId 中」的节点,O(n) 一次遍历。
     *
     * @param nodes          节点列表
     * @param idGetter       取节点id
     * @param parentIdGetter 取父节点id
     */
    public static <T, K> List<T> getLeafNodes(
            List<T> nodes,
            Function<T, K> idGetter,
            Function<T, K> parentIdGetter) {
        if (nodes == null || nodes.isEmpty()) {
            return new ArrayList<>();
        }
        Set<K> parentIds = nodes.stream()
                .map(parentIdGetter)
                .filter(Objects::nonNull)
                .collect(Collectors.toSet());
        return nodes.stream()
                .filter(n -> !parentIds.contains(idGetter.apply(n)))
                .collect(Collectors.toList());
    }
 
    /* ================== SysDept 便捷方法 ================== */
 
    /**
     * 根据 SysDept.parentId 构造部门树
     */
    public static List<SysDept> buildDeptTree(List<SysDept> depts) {
        return buildTree(depts, SysDept::getDeptId, SysDept::getParentId, SysDept::setChildren);
    }
 
    /**
     * 一次性获取所有部门叶子节点
     */
    public static List<SysDept> getLeafDepts(List<SysDept> depts) {
        return getLeafNodes(depts, SysDept::getHisDeptId, SysDept::getHisParentId);
    }
}