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
上一篇:识别视频中的人数并统计出来


下一篇:python+pygame实现五子棋网络对战之一-一、