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

Java实现Node类构建父子树 统计指定节点所有后代节点方案

问题背景

需要实现Node类构建多叉树形结构,基础规则为每个节点可持有多个子节点,通过实例化多个节点即可组装为完整树,示例结构如下:

node1 (根节点) 子节点为node2、node3
node2 子节点为node4、node5

核心需求为两点:

  • 修复现有代码方法签名与调用不匹配的编译问题
  • 实现查询指定节点下所有后代节点的功能(以上述示例为例,node1的后代共4个:直接子节点node2、node3,加上node2的子节点node4、node5)

现有给出的Node类、树构建业务逻辑、CSV数据源存在逻辑bug,无法正常运行。

现有代码核心问题
  • findNodeByName方法仅提供双参数签名,但业务代码中大量调用单参数版本,直接报编译错误
  • findNodeByName递归逻辑存在隐患:遍历子节点时依赖当前实例的children属性,而非传入的遍历起始节点的子节点集合,跨节点调用时会出现查找失效
  • 未实现全量后代节点查询、统计的相关逻辑
  • 存在无用导入,空描述字段未做判空处理,容易触发空指针
  • CSV解析逻辑未处理引号包裹字段、Windows换行符\r,容易出现字段解析错误
修正实现方案

1. 修正Node类

删除无用导入,修复查找方法逻辑,新增后代节点收集方法,最终代码如下:

package ex1;

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

public class Node {

  private String name;
  private String description;
  private ArrayList<Node> children = new ArrayList<>();

  Node(String name, String description){
    this.name = name;
    // 空描述默认赋值为空串,避免空指针
    this.description = description == null ? "" : description;
  }

  private void setName(String name){
    this.name = name;
  }

  private void setDescription(String description) {
    this.description = description == null ? "" : description;
  }

  public void addChildren(Node child) {
    this.children.add(child);
  }

  public String getName() {
    return this.name;
  }

  public String getDescription() {
    return this.description;
  }

  public boolean hasDescription() {
    return !description.isEmpty();
  }

  public Collection<Node> getChildren() {
    return this.children;
  }

  // 单参数重载方法,匹配业务层调用逻辑,默认从当前节点开始查找
  public Node findNodeByName(String name){
    return findNodeByName(name, this);
  }

  // 内部递归查找逻辑,遍历指定起始节点的所有子树
  private Node findNodeByName(String name, Node t){
    if(t.getName().equals(name)){
      return t;
    }
    for(Node c: t.getChildren()){
      Node ret = c.findNodeByName(name, c);
      if(ret != null){
        return ret;
      }
    }
    return null;
  }

  // 获取当前节点下所有后代节点(包含所有层级的子节点)
  public List<Node> getAllDescendants(){
    List<Node> descendants = new ArrayList<>();
    collectDescendants(this, descendants);
    return descendants;
  }

  // 深度优先递归收集所有后代
  private void collectDescendants(Node current, List<Node> collector){
    for (Node child : current.getChildren()) {
      collector.add(child);
      collectDescendants(child, collector);
    }
  }

  // 统计后代节点总数量
  public int countAllDescendants(){
    return getAllDescendants().size();
  }

  // 要求保留的方法,不做修改
  private String toString(int indentNo) {
    String indent = "\t".repeat(indentNo);
    StringBuffer b = new StringBuffer();
    b.append(indent);
    b.append(getClass().getSimpleName() + " '" + getName() + "' ");

    if (hasDescription()) {
      b.append("(description: " + getDescription() + ")");
    }

    b.append("\n");

    for (Node node : getChildren()) {
      b.append(node.toString(indentNo + 1));
    }

    return b.toString();
  }

  @Override
  public String toString() {
    return toString(0);
  }
}

2. 修正树构建业务逻辑

调整CSV解析逻辑,处理换行符、引号字段问题,适配修正后的Node类方法:

Path path = Path.of(pathname);

String fileContent = null;
try {
   fileContent = Files.readString(path);
   // 替换Windows换行符,避免字段携带多余\r字符
   fileContent = fileContent.replace("\r", "");
} catch (IOException e) {
   throw new RuntimeException(e);
}
List<String> lines = new ArrayList<>(Arrays.asList(fileContent.split("\n")));
// 过滤空行
lines.removeIf(String::isBlank);
// split传-1保留空字段,避免数组长度不足
String[] firstLine = lines.get(0).split(",", -1);
// 去掉描述字段包裹的引号
String rootName = firstLine[0];
String rootDesc = firstLine[1].replace("\"", "");
Node parentNode = new Node(rootName, rootDesc);
lines.remove(0);

Node tmpNode;
for(String line: lines) {
   String[] params = line.split(",", -1);
   String nodeName = params[0];
   String nodeDesc = params[1].replace("\"", "");
   String parentName = params[2];
   // 校验:父节点必须存在、新节点名不重复
   if (parentNode.findNodeByName(parentName)==null || parentNode.findNodeByName(nodeName)!=null){
         throw new IOException("节点配置非法:父节点不存在或节点名重复");
   }
   tmpNode = parentNode.findNodeByName(parentName);
   tmpNode.addChildren(new Node(nodeName,nodeDesc));
}
功能验证

基于给出的CSV数据源构建完成树后,可直接调用方法验证功能:

  • 根节点uni调用countAllDescendants()返回5,对应5个后代节点:firstyear、secondyear、analysis、db、ai
  • 节点firstyear调用countAllDescendants()返回2,对应子节点analysis、db
  • 节点secondyear调用countAllDescendants()返回1,对应子节点ai
  • 任意节点调用getAllDescendants()可直接获取所有后代节点的实例列表,可按需做后续过滤、遍历操作。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 12:12:21