java 生成树结构(可通过id、code)公共方法
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang.mutable.MutableLong;
import org.jetbrains.annotations.NotNull;
import org.springframework.lang.NonNull;
import org.springframework.lang.Nullable;
import org.springframework.util.StringUtils;
import java.util.*;
import java.util.function.BiConsumer;
import java.util.function.Function;
import java.util.stream.Collectors;
/**
* 树结构工具类
*
* @author Mr丶s
* @date 2024/7/16 下午4:58
* @description
*/
@SuppressWarnings("unused")
public class TreeUtil {
/**
* ParentId 形式树构建,当一个节点的 ParentId 不为任何其他节点的 ID,该节点为根节点
*
* @param list 所有参与构建的节点
* @param getId 获取 ID
* @param getParentId 获取父级 ID
* @param comparator 同级排序(非必传)
* @param setSub 设置子级
* @param <T> 树节点类型
* @param <I> 树节点 ID 类型
* @return 树
*/
public static <T, I> List<T> buildByParentId(@NonNull List<T> list,
@NonNull Function<T, I> getId,
@NonNull Function<T, I> getParentId,
@Nullable Comparator<T> comparator,
@NonNull BiConsumer<T, List<T>> setSub) {
List<T> tree = rootListByParentId(list, getId, getParentId);
sortList(tree, comparator);
tree.forEach(n -> buildByParentId(n, list, getId, getParentId, comparator, setSub));
return tree;
}
/**
* 编码形式树构建,当一个节点的编码不以任何其他节点编码为前缀时,该节点为根节点
* <p>
* 所有节点的子节点树形必须不为 null
*
* @param list 所有参与构建的节点
* @param getCode 获取编码
* @param comparator 同级排序(非必传)
* @param getSub 获取子级 List
* @param <T> 树节点类型
* @param <C> 树节点编码类型
* @return 树
*/
public static <T, C extends String> List<T> buildByCode(@NonNull List<T> list,
@NonNull Function<T, C> getCode,
@Nullable Comparator<T> comparator,
@NonNull Function<T, List<T>> getSub,
@NonNull BiConsumer<T, List<T>> setSub) {
Map<C, List<T>> code2List = groupByCode(list, getCode);
List<T> tree = new ArrayList<>();
code2List.forEach((k, v) -> tree.add(buildNodeByCode(v, getCode, getSub, setSub)));
sortTree(tree, comparator, getSub);
return tree;
}
/**
* 获取父级
*
* @param list list
* @param ids ids
* @param idExtractor id策略
* @param parentIdExtractor parentId策略
* @param containSelf 是否包含当前ids的对象
* @return 父级list
*/
public static <T, R> List<T> getParent(List<T> list, List<R> ids,
Function<? super T, ? extends R> idExtractor,
Function<? super T, ? extends R> parentIdExtractor,
boolean containSelf) {
if (CollectionUtils.isEmpty(list) || CollectionUtils.isEmpty(ids)) {
return new ArrayList<>();
}
List<T> result = new ArrayList<>();
Map<? extends R, T> map = list.stream()
.collect(Collectors.toMap(idExtractor, Function.identity()));
for (R id : ids) {
for (T item : list) {
if (!Objects.equals(id, idExtractor.apply(item))) {
continue;
}
int i = 1;
T parent = item;
while (parent != null) {
if (i == 1) {
if (containSelf) {
result.add(parent);
}
} else {
result.add(parent);
}
parent = map.get(parentIdExtractor.apply(parent));
i++;
}
}
}
result = result.stream()
.filter(ObjectUtils.distinctByKey(idExtractor))
.collect(Collectors.toList());
return result;
}
/**
* 获取子级
*
* @param list list
* @param ids ids
* @param idExtractor id策略
* @param parentIdExtractor parentId策略
* @param containSelf 是否包含当前id的对象
* @return 子级list
*/
public static <T, R> List<T> getChildren(List<T> list, List<R> ids,
Function<? super T, ? extends R> idExtractor,
Function<? super T, ? extends R> parentIdExtractor,
boolean containSelf) {
if (CollectionUtils.isEmpty(list) || CollectionUtils.isEmpty(ids)) {
return new ArrayList<>();
}
List<T> result = new ArrayList<>();
if (containSelf) {
list.stream()
.filter(c -> ids.contains(idExtractor.apply(c)))
.forEach(result::add);
}
Map<? extends R, List<T>> map = list.stream()
.filter(c -> Objects.nonNull(parentIdExtractor.apply(c)))
.collect(Collectors.groupingBy(parentIdExtractor));
List<R> parentIds = new ArrayList<>(ids);
for (int i = 0; i < parentIds.size(); i++) {
R parentId = parentIds.get(i);
List<T> childList = map.get(parentId);
if (childList == null) {
continue;
}
result.addAll(childList);
childList.forEach(c -> parentIds.add(idExtractor.apply(c)));
}
result = result.stream()
.filter(ObjectUtils.distinctByKey(idExtractor))
.collect(Collectors.toList());
return result;
}
/**
* 顶层节点开始搜索所有具有指定属性的节点
*
* @param tree 需要搜索的树
* @param getKey 获取属性
* @param key 属性值
* @param <T> 树节点类型
* @param <I> 属性值类型
* @return 所有满足指定属性的节点
*/
public static <T, I> List<T> searchTree4All(@NonNull List<T> tree, @NonNull Function<T, I> getKey,
@NonNull Function<T, List<T>> getSub, @NonNull I key) {
List<T> matched = new ArrayList<>();
tree.forEach(n -> {
I currentKey = getKey.apply(n);
if (currentKey != null && currentKey.equals(key)) {
matched.add(n);
}
List<T> sub = getSub.apply(n);
if (sub != null && sub.size() != 0) {
matched.addAll(searchTree4All(sub, getKey, getSub, key));
}
});
return matched;
}
/**
* 顶层节点开始搜索第一个具有指定属性的节点
*
* @param tree 需要搜索的树
* @param getKey 获取属性
* @param key 属性值
* @param <T> 树节点类型
* @param <I> 属性值类型
* @return 第一个具有指定属性的节点
*/
public static <T, I> Optional<T> searchTree4One(@NonNull List<T> tree, @NotNull Function<T, I> getKey,
@NonNull Function<T, List<T>> getSub, @NotNull I key) {
for (T n : tree) {
I currentKey = getKey.apply(n);
if (currentKey != null && currentKey.equals(key)) {
return Optional.of(n);
}
List<T> sub = getSub.apply(n);
if (sub != null && sub.size() != 0) {
Optional<T> subSearchResult = searchTree4One(sub, getKey, getSub, key);
if (subSearchResult.isPresent()) {
return subSearchResult;
}
}
}
return Optional.empty();
}
/**
* 将树转换为列表(非对象拷贝,下级属性中仍存在相关引用)
*
* @param tree 需要展开的树
* @param getSub 获取树的下级
* @param <T> 树节点类型
* @return 树列表
*/
public static <T> List<T> tree2List(@NonNull List<T> tree, @NonNull Function<T, List<T>> getSub) {
List<T> list = new ArrayList<>();
tree.forEach(n -> {
list.add(n);
List<T> sub = getSub.apply(n);
if (sub != null && sub.size() != 0) {
list.addAll(tree2List(sub, getSub));
}
});
return list;
}
/**
* 为树节点添加随机 ID
*
* @param tree 需要添加节点的树
* @param getSub 获取树节点的下级
* @param setId 设置树节点的 ID
* @param setParentId 设置树节点的父 ID
* @param parentId 初始 parentId,即根节点的 parentId 值(为 null 时为 0)
* @param idCounter ID 计数器(为 null 时 id 从 1 开始)
* @param <T> 树节点类型
*/
public static <T> void addRandomId(@NonNull List<T> tree, @NonNull Function<T, List<T>> getSub,
@NonNull BiConsumer<T, Long> setId, @NonNull BiConsumer<T, Long> setParentId,
@Nullable Long parentId, @Nullable MutableLong idCounter) {
parentId = parentId == null ? 0L : parentId;
idCounter = idCounter == null ? new MutableLong(1L) : idCounter;
for (T n : tree) {
long id = idCounter.longValue();
idCounter.increment();
setId.accept(n, id);
setParentId.accept(n, parentId);
List<T> sub = getSub.apply(n);
if (sub != null && sub.size() != 0) {
addRandomId(sub, getSub, setId, setParentId, id, idCounter);
}
}
}
/**
* 在树中按名称进行搜索
*
* @param tree 原始树
* @param getSub 获取子节点
* @param getName 获取名称
* @param searchName 搜索名称
* @param reserveChild 父节点名称匹配时是否保留所有子节点
* @param <T> 树类型泛型
*/
public static <T> void filterTreeByName(@NonNull List<T> tree, @NonNull Function<T, List<T>> getSub,
@NonNull Function<T, String> getName, @NonNull String searchName,
@NonNull Boolean reserveChild) {
if (!StringUtils.hasLength(searchName)) {
return;
}
for (Iterator<T> iterator = tree.iterator(); iterator.hasNext(); ) {
T n = iterator.next