You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何将层级数据库记录转换为Java层级对象?

实现扁平数据库记录到嵌套层级对象的转换(Java)

嘿,这个问题其实很常见,核心思路就是先建立节点的快速映射,再逐个关联父子关系,我给你一步步拆解实现方案:

第一步:调整你的实体类(可选但更友好)

你定义的SomeTable结构没问题,不过把list改成children更语义化,另外加上getter/setter方便操作,也可以用Lombok的@Data简化代码:

import java.util.ArrayList;
import java.util.List;

// 用Lombok的话只需要加@Data,不用手动写getter/setter和toString
// @Data
public class SomeTable {
    private Integer parentId;
    private Integer id;
    private List<SomeTable> children = new ArrayList<>(); // 提前初始化空列表,避免空指针

    // 手动实现getter/setter
    public Integer getParentId() { return parentId; }
    public void setParentId(Integer parentId) { this.parentId = parentId; }
    public Integer getId() { return id; }
    public void setId(Integer id) { this.id = id; }
    public List<SomeTable> getChildren() { return children; }
    public void setChildren(List<SomeTable> children) { this.children = children; }

    // 自定义toString,输出你想要的嵌套格式
    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append(id).append(" : [ ");
        for (int i = 0; i < children.size(); i++) {
            sb.append(children.get(i).toString());
            if (i != children.size() - 1) {
                sb.append(" ");
            }
        }
        sb.append(" ]");
        return sb.toString();
    }
}

第二步:核心转换逻辑

主要分三步完成转换,效率和可读性都拉满:

  1. 把所有节点存入Map,用id作为key,方便快速查找父节点
  2. 遍历每个节点,将子节点挂载到对应父节点的children列表中
  3. 收集所有根节点(parentId=0的节点)

代码实现如下:

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class HierarchyConverter {
    public static List<SomeTable> buildHierarchy(List<SomeTable> flatData) {
        // 1. 构建节点映射表,O(n)时间复杂度,快速查找父节点
        Map<Integer, SomeTable> nodeMap = new HashMap<>();
        for (SomeTable node : flatData) {
            nodeMap.put(node.getId(), node);
        }

        // 2. 关联父子节点,同时收集根节点
        List<SomeTable> rootNodes = new ArrayList<>();
        for (SomeTable node : flatData) {
            Integer parentId = node.getParentId();
            if (parentId == 0) {
                // 父节点为0,属于根节点
                rootNodes.add(node);
            } else {
                // 找到父节点,将当前节点加入父节点的子列表
                SomeTable parentNode = nodeMap.get(parentId);
                if (parentNode != null) { // 兼容数据库可能存在的无效数据(父节点不存在)
                    parentNode.getChildren().add(node);
                }
            }
        }

        return rootNodes;
    }

    // 测试用例:模拟数据库查询的扁平数据
    public static void main(String[] args) {
        List<SomeTable> flatData = new ArrayList<>();
        flatData.add(createNode(0, 1));
        flatData.add(createNode(1, 11));
        flatData.add(createNode(1, 12));
        flatData.add(createNode(11, 111));
        flatData.add(createNode(0, 2));
        flatData.add(createNode(2, 21));
        flatData.add(createNode(21, 211));

        // 构建层级结构
        List<SomeTable> hierarchy = buildHierarchy(flatData);

        // 输出你想要的格式
        System.out.println("[ ");
        for (int i = 0; i < hierarchy.size(); i++) {
            System.out.println("  " + hierarchy.get(i).toString());
            if (i != hierarchy.size() - 1) {
                System.out.println("  ");
            }
        }
        System.out.println(" ]");
    }

    // 工具方法:快速创建节点
    private static SomeTable createNode(Integer parentId, Integer id) {
        SomeTable node = new SomeTable();
        node.setParentId(parentId);
        node.setId(id);
        return node;
    }
}

第三步:运行结果

执行上述代码后,输出结果完全匹配你想要的格式:

[ 
  1 : [ 11 : [ 111 : [  ] ] 12 : [  ] ]
  
  2 : [ 21 : [ 211 : [  ] ] ]
 ]

为什么这么设计?

  • 用Map存储节点:把查找父节点的时间复杂度从O(n²)降到O(n),数据量大时效率提升非常明显
  • 提前初始化children列表:避免后续添加子节点时出现空指针异常
  • 加入空值判断:兼容数据库可能存在的无效数据(比如子节点的父ID不存在)
  • 逻辑解耦:映射和关联两步分开,代码可读性更高,也方便后续扩展

内容的提问来源于stack exchange,提问作者JoonT

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 04:27:32