private IndicatorTreeDTO generateIndicatorTree(List<IndicatorTreeDTO> indicatorTreeDTOList) {
        // 创建ID到IndicatorTreeDTO的映射
        Map<String, IndicatorTreeDTO> idMap = new HashMap<>();
        // 创建parentId到children的映射
        Map<String, List<IndicatorTreeDTO>> parentIdMap = new HashMap<>();

        // 使用一次forEach填充两个映射
        indicatorTreeDTOList.forEach(indicator -> {
            idMap.put(indicator.getId(), indicator);
            // 防止自我引用
            if (isChildren(indicator)) {
                parentIdMap.computeIfAbsent(indicator.getParent_id(), k -> new ArrayList<>()).add(indicator);
            }
        });
        // 顶点节点列表
        List<IndicatorTreeDTO> topChildren = new ArrayList<>();
        parentIdMap.forEach((parentId, children) -> {
            // 拿IndicatorTreeDTO的parent_id  id  来组装树, 如果parent_id找不到 说明是第一层
            IndicatorTreeDTO parent = idMap.get(parentId);
            if (parent != null) {
                parent.setChildren(children);
            } else {
                topChildren.addAll(children);
            }
        });
        IndicatorTreeDTO indicatorTreeDTO = new IndicatorTreeDTO();
        indicatorTreeDTO.setChildren(topChildren);
        return indicatorTreeDTO;
    }

    private boolean isChildren(IndicatorTreeDTO indicator) {
        return indicator.getParent_id() != null && !indicator.getId().equals(indicator.getParent_id());
    }

在当前的实现中,我们已经处理了:

  1. 当parent_id为空时,节点被认为是顶级节点。
  2. 当parent_id等于自身ID时,防止自我引用。 然而,我们还应考虑以下可能的bug和问题:
  3. 循环引用:如果两个或者更多节点互相成为彼此的父节点,将创建一个不可解的循环。为了防止这个,我们需要在设置父子关系前进行检查。
  4. ID或parent_id的一致性问题:我们假设每个节点的ID和parent_id始终有效和一致。如果有些节点的ID或者parent_id无效(例如,找不到对应的节点),那么我们可能无法构建出正确的树状结构。
  5. **性能问题:**如果树的深度很大或拥有大量的节点,那么内存和处理时间可能会增加。在这种情况下,我们可能需要寻找或设计一种更有效率的算法来构造树。

## 循环引用

使用了深度优先搜索(DFS)来检测图中是否存在环

private boolean detectCycle(Map<String, List<IndicatorTreeDTO>> childrenMap, String nodeId, Set<String> visited, Set<String> recStack) {
    if (recStack.contains(nodeId)) {
        return true;
    }

    if (visited.contains(nodeId)) {
        return false;
    }

    visited.add(nodeId);
    recStack.add(nodeId);

    List<IndicatorTreeDTO> children = childrenMap.get(nodeId);
    if (children != null) {
        for (IndicatorTreeDTO child : children) {
            if (detectCycle(childrenMap, child.getId(), visited, recStack)) {
                return true;
            }
        }
    }

    recStack.remove(nodeId);
    return false;
}

private IndicatorTreeDTO generateIndicatorTree(List<IndicatorTreeDTO> indicatorTreeDTOList) {
    Map<String, IndicatorTreeDTO> idMap = new HashMap<>();
    Map<String, List<IndicatorTreeDTO>> parentIdMap = new HashMap<>();

    indicatorTreeDTOList.forEach(indicator -> {
        idMap.put(indicator.getId(), indicator);
        parentIdMap.computeIfAbsent(indicator.getParent_id(), k -> new ArrayList<>()).add(indicator);
    });
// 重新构建树之前检查
    Set<String> visited = new HashSet<>();
    Set<String> recStack = new HashSet<>();
    for (IndicatorTreeDTO node : indicatorTreeDTOList) {
        if (detectCycle(parentIdMap, node.getId(), visited, recStack)) {
            throw new IllegalArgumentException(String.format("Circular reference detected with id: %s", node.getId()));
        }
    }
..........
}