欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 科技 > 名人名企 > Java高效构建树形结构——异步加载子节点的实现方案

Java高效构建树形结构——异步加载子节点的实现方案

2025/5/18 11:04:22 来源:https://blog.csdn.net/Zyw907155124/article/details/146321213  浏览:    关键词:Java高效构建树形结构——异步加载子节点的实现方案

文章目录

        • **一、改造节点模型**
        • **二、定义异步加载接口**
        • **三、改造树构建工具类**
        • **四、实现按需加载逻辑**
        • **五、使用示例**
          • 1. 定义异步加载器(模拟数据库查询)
          • 2. 构建初始树(仅根节点)
          • 3. 前端触发加载某个节点的子节点
        • **六、关键优化点**
        • **七、适用场景**

Java高效构建树形结构的秘密:函数式编程 + 预排序的完美结合

要实现树形结构的异步加载子节点,核心思路是将子节点的获取过程延迟化,通过回调或 CompletableFuture 实现非阻塞加载。以下是具体实现步骤:

一、改造节点模型

在节点对象中增加两个字段,标识子节点是否已加载:

class TbMenuVo {// 原有字段...private volatile boolean childrenLoaded = false; // 子节点是否已加载private volatile boolean loading = false;        // 是否正在加载中(防重复请求)
}

二、定义异步加载接口

新增一个函数式接口,用于按需从外部获取子节点数据(例如从数据库或远程服务):

@FunctionalInterface
public interface AsyncChildrenLoader<T, ID> {CompletableFuture<List<T>> loadChildren(ID parentId);
}

三、改造树构建工具类

修改 buildTree 方法,支持异步加载逻辑:

 public static <T, ID> List<T> buildTreeAsync(List<T> list,Function<T, ID> getId,Function<T, ID> getParentId,BiConsumer<T, List<T>> setChildren,Predicate<T> rootPredicate,Comparator<T> comparator,AsyncChildrenLoader<T, ID> childrenLoader // 新增异步加载器
) {// 预构建父节点映射表(仅处理已知数据)Map<ID, List<T>> parentMap = list.stream().filter(item -> getParentId.apply(item) != null).collect(Collectors.groupingBy(getParentId));return list.stream().filter(rootPredicate).map(root -> {// 初始化时仅设置直接子节点(若有)ID rootId = getId.apply(root);List<T> immediateChildren = parentMap.getOrDefault(rootId, Collections.emptyList());setChildren.accept(root, immediateChildren);return root;}).sorted(comparator).collect(Collectors.toList());
}

四、实现按需加载逻辑

为节点添加异步加载方法,触发时从 AsyncChildrenLoader 获取数据:

public static <T, ID> CompletableFuture<Void> loadChildrenAsync(T node,Function<T, ID> getId,BiConsumer<T, List<T>> setChildren,AsyncChildrenLoader<T, ID> childrenLoader,Comparator<T> comparator
) {ID nodeId = getId.apply(node);if (node instanceof TbMenuVo) {TbMenuVo menuNode = (TbMenuVo) node;if (menuNode.isChildrenLoaded() || menuNode.isLoading()) {return CompletableFuture.completedFuture(null);}menuNode.setLoading(true);}return childrenLoader.loadChildren(nodeId).thenApply(children -> {// 对子节点排序if (comparator != null) {children.sort(comparator);}// 设置子节点并标记为已加载setChildren.accept(node, children);if (node instanceof TbMenuVo) {TbMenuVo menuNode = (TbMenuVo) node;menuNode.setChildrenLoaded(true);menuNode.setLoading(false);}return null;}).exceptionally(ex -> {// 处理异常(如记录日志)if (node instanceof TbMenuVo) {((TbMenuVo) node).setLoading(false);}return null;});
}

五、使用示例
1. 定义异步加载器(模拟数据库查询)
AsyncChildrenLoader<TbMenuVo, Long> loader = parentId -> CompletableFuture.supplyAsync(() -> {// 模拟根据parentId查询数据库List<TbMenuVo> children = database.queryChildrenByParentId(parentId);return children;});
2. 构建初始树(仅根节点)
List<TbMenuVo> tree = TreeBuilderOptimized.buildTreeAsync(lists, TbMenuVo::getId, TbMenuVo::getParentId,TbMenuVo::setChildren,item -> item.getParentId() == 0,Comparator.comparingInt(TbMenuVo::getSortOrder),loader
);
3. 前端触发加载某个节点的子节点
// 当用户点击展开节点时触发
public void onExpandNode(TbMenuVo node) {if (!node.isChildrenLoaded()) {TreeBuilderOptimized.loadChildrenAsync(node, TbMenuVo::getId,TbMenuVo::setChildren,loader,Comparator.comparingInt(TbMenuVo::getSortOrder)).thenRun(() -> {// 通知UI更新(例如前端重新渲染)refreshUI();});}
}

六、关键优化点
  1. 防重复请求:通过 loading 标志位避免并发重复加载
  2. 线程安全:使用 volatile 确保状态可见性
  3. 异常处理:捕获异步加载中的异常并恢复状态
  4. 与现有工具兼容:保留同步构建方法,异步方法作为扩展

七、适用场景
  • 大型树形结构数据(如万级以上节点)
  • 需要动态加载的子节点(如文件系统目录树)
  • 前端分批次渲染避免卡顿

通过以上改造,工具类既能保留原有高性能构建能力,又能灵活支持异步加载需求,实现“按需扩展”的树形数据处理!

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词