如何从PostgreSQL重建无重复节点的树形结构?
解决方案
1. 直接修复:加载前清空集合
当前代码中tree是类级别的持久化集合,每次调用loadTree都会在原有数据上追加新节点,这是重复问题的核心原因。在加载新数据前清空集合即可解决:
public void loadTree(List<NodeEntity> nodes) { // 加载前清空原有数据 tree.clear(); for (NodeEntity node : nodes) { if (node.getParentId() != null) { tree.putIfAbsent(node.getParentId(), new ArrayList<>()); tree.get(node.getParentId()).add(node.getId()); } } }
2. 更安全的方案:每次加载创建新集合
如果存在其他模块依赖这个tree集合,不希望直接清空原有数据,可以每次加载时创建新的Map并替换原有引用(多线程环境需注意线程安全,可使用AtomicReference或加锁):
private Map<Long, List<Long>> tree = new HashMap<>(); public void loadTree(List<NodeEntity> nodes) { Map<Long, List<Long>> newTree = new HashMap<>(); for (NodeEntity node : nodes) { if (node.getParentId() != null) { newTree.putIfAbsent(node.getParentId(), new ArrayList<>()); newTree.get(node.getParentId()).add(node.getId()); } } // 原子替换原有集合 this.tree = newTree; }
3. 根源性优化:避免重复数据源
如果数据库查询可能返回重复记录(比如批量导入或关联查询导致的重复),可以在查询阶段去重:
SELECT DISTINCT id, value, parent_id FROM nodes;
也可以在Java代码中对节点列表去重,基于主键id过滤:
List<NodeEntity> distinctNodes = nodes.stream() .collect(Collectors.toMap(NodeEntity::getId, n -> n, (existing, replacement) -> existing)) .values() .stream() .collect(Collectors.toList());
4. 推荐的完整树形构建方式
如果业务需要直接操作树形节点对象(而非仅ID映射),可以定义包含子节点的实体类,一次性构建完整树形结构,同时天然避免重复:
public class Node { private Long id; private String value; private List<Node> children = new ArrayList<>(); // getter、setter方法 }
构建逻辑:
public List<Node> buildFullTree(List<NodeEntity> nodes) { Map<Long, Node> nodeMap = new HashMap<>(); List<Node> rootNodes = new ArrayList<>(); // 先将所有实体转换为Node对象并存入Map for (NodeEntity entity : nodes) { Node node = new Node(); node.setId(entity.getId()); node.setValue(entity.getValue()); nodeMap.put(entity.getId(), node); } // 绑定父子关系,同时检查避免重复添加 for (NodeEntity entity : nodes) { if (entity.getParentId() == null) { rootNodes.add(nodeMap.get(entity.getId())); } else { Node parentNode = nodeMap.get(entity.getParentId()); if (parentNode != null) { Node childNode = nodeMap.get(entity.getId()); // 检查子节点是否已存在,避免重复 boolean isDuplicate = parentNode.getChildren().stream() .anyMatch(child -> child.getId().equals(childNode.getId())); if (!isDuplicate) { parentNode.getChildren().add(childNode); } } } } return rootNodes; }
这种方式构建的树形结构更易用,且能从根源上避免子节点重复问题。
内容的提问来源于stack exchange,提问作者MARIO JOSE BARRERA LINAREZ
相关产品推荐
相关产品推荐

