/**
* 遍历原始数据列表,按照给定的父ID获取函数对节点进行分组,构建一个以父ID为键、子节点列表为值的映射表。
* 这样可以方便地根据节点的子ID快速找到其所有子节点。
*
* 初始化一个空的结果列表,用于存放最终形成的树形结构。
*
* 遍历原始数据列表中的每个节点:
* a. 使用子ID获取函数获取当前节点的子ID。
* b. 从映射表中查找与当前节点子ID对应的子节点列表。如果存在且非空,说明当前节点有子节点,
* 则使用给定的子节点设置消费者将子节点列表赋给当前节点。
* c. 使用父ID获取函数获取当前节点的父ID。
* d. 根据传入的根节点标识判断当前节点是否应被加入结果列表:
* - 如果根节点标识为null,表示返回整个树形结构,因此将所有节点都加入结果列表;
* - 否则,只有当当前节点的父ID等于根节点标识时,才将当前节点加入结果列表。
*
* 最终返回构建好的树形结构列表。
*/
/**
* 将数据转换为树状数据
* @param list 原始数据列表
* @param rootTrait 根节点标识(如果为null,则返回所有节点组成的树)
* @param parentValueGetter 获取节点父ID的函数
* @param childValueGetter 获取节点子ID的函数
* @param childSetter 设置节点子节点列表的消费者
* @param <T> 节点类型
* @param <R> ID类型
* @return 树形数据
*/
public static <T, R> List<T> buildTree(List<T> list, R rootTrait,
Function<T, R> parentValueGetter,
Function<T, R> childValueGetter,
BiConsumer<T, List<T>> childSetter) {
Map<R, List<T>> nodesByParentId = list.stream()
.collect(Collectors.groupingBy(parentValueGetter));
List<T> result = new ArrayList<>();
for (T node : list) {
R nodeId = childValueGetter.apply(node);
List<T> children = nodesByParentId.get(nodeId);
if (CollUtil.isNotEmpty(children)) {
childSetter.accept(node, children);
}
R parentNodeId = parentValueGetter.apply(node);
if (rootTrait == null || Objects.equals(parentNodeId, rootTrait)) {
result.add(node);
}
}
return result;
}