当前位置: 首页 > article >正文

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. 与现有工具兼容:保留同步构建方法,异步方法作为扩展

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

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


http://www.kler.cn/a/589500.html

相关文章:

  • “Failed to Load SteamUI.dll” 错误详解:全面解析与高效解决方案,助你快速修复 Steam 客户端问题
  • python拉取大视频导入deepseek大模型解决方案
  • ubuntu20.04下如何防止同一类型串口设备插入USB口无法区分到底是从/dev/ttyUSB0还是/dev/ttyUSB1读取数据
  • Java 8 Stream API:传统实现和流式编程的范式对比
  • 道格拉斯-普克算法
  • Android Room 框架公共模块源码深度剖析(四)
  • linux环境安装qnn_sdk 采坑记录
  • 事件驱动架构(EDA):微服务世界的未来趋势
  • LeetCode[206]反转链表
  • MySQL连接较慢原因分析及解决措施
  • C++基础 [五] - String的模拟实现
  • FlinkCDC 达梦数据库实时同步详解
  • java,poi,提取ppt文件中的文字内容
  • LLaMA-Factory微调sft Qwen2.5-VL-7B-Instruct
  • 【etcd】
  • 【通义千问】蓝耘智算 | 智启未来:蓝耘MaaS×通义QwQ-32B引领AI开发生产力
  • 本地部署DeepSeek-R1(Dify升级最新版本、新增插件功能)
  • 【嵌入式硬件】三款DCDC调试笔记
  • 【地图 Map】——8
  • C++进阶——AVL树的实现