《《《用java递归查询树节点返回List,并可以根据name查询节点

来自豆包

一、核心逻辑说明

  1. 递归遍历:采用先序遍历(根→子),保证节点遍历顺序符合直觉;
  2. 名称查询:支持精确匹配(名称完全一致)和模糊匹配(包含目标字符串,忽略大小写);
  3. 空值防护:处理节点为 null、名称为空 / 空白字符串的边界场景;
  4. 灵活扩展:可传入任意节点作为起始点(整棵树 / 某个子树)。

二、完整代码实现

1. 树节点实体类(TreeNode)

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

/**
 * 树节点基础实体
 */
public class TreeNode {
    // 节点唯一标识
    private Long id;
    // 父节点ID
    private Long parentId;
    // 节点名称(用于查询)
    private String name;
    // 子节点列表
    private List<TreeNode> children;

    // 构造器:初始化子节点列表,避免空指针
    public TreeNode(Long id, Long parentId, String name) {
        this.id = id;
        this.parentId = parentId;
        this.name = name;
        this.children = new ArrayList<>();
    }

    // Getter & Setter
    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public Long getParentId() {
        return parentId;
    }

    public void setParentId(Long parentId) {
        this.parentId = parentId;
    }

    public String getName() {
        return name;
    }

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

    public List<TreeNode> getChildren() {
        return children;
    }

    public void setChildren(List<TreeNode> children) {
        this.children = children;
    }

    // 重写toString,方便打印结果
    @Override
    public String toString() {
        return "TreeNode{" +
                "id=" + id +
                ", parentId=" + parentId +
                ", name='" + name + '\'' +
                '}';
    }
}

2. 树节点递归工具类(TreeUtils)

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

/**
 * 树节点递归遍历/查询工具类
 */
public class TreeUtils {

    // ==================== 基础递归遍历:返回全量节点List ====================
    /**
     * 递归先序遍历(根→子),返回所有节点的扁平化List
     * @param rootNode 起始节点(根节点/任意子节点)
     * @return 全量节点列表
     */
    public static List<TreeNode> listAllNodes(TreeNode rootNode) {
        List<TreeNode> result = new ArrayList<>();
        traversePreOrder(rootNode, result);
        return result;
    }

    /**
     * 私有递归方法:先序遍历核心逻辑
     */
    private static void traversePreOrder(TreeNode currentNode, List<TreeNode> result) {
        if (currentNode == null) {
            return;
        }
        // 先加入当前节点
        result.add(currentNode);
        // 递归遍历子节点
        List<TreeNode> children = currentNode.getChildren();
        if (children != null && !children.isEmpty()) {
            for (TreeNode child : children) {
                traversePreOrder(child, result);
            }
        }
    }

    // ==================== 按名称查询节点:精确/模糊 ====================
    /**
     * 按名称精确匹配查询节点
     * @param rootNode   起始节点
     * @param targetName 目标名称(trim后完全一致)
     * @return 匹配的节点列表
     */
    public static List<TreeNode> searchByExactName(TreeNode rootNode, String targetName) {
        List<TreeNode> result = new ArrayList<>();
        searchByName(rootNode, targetName, true, result);
        return result;
    }

    /**
     * 按名称模糊匹配查询节点(忽略大小写,包含目标字符串)
     * @param rootNode   起始节点
     * @param targetName 目标名称
     * @return 匹配的节点列表
     */
    public static List<TreeNode> searchByFuzzyName(TreeNode rootNode, String targetName) {
        List<TreeNode> result = new ArrayList<>();
        searchByName(rootNode, targetName, false, result);
        return result;
    }

    /**
     * 私有递归方法:名称匹配核心逻辑
     * @param currentNode 当前遍历节点
     * @param targetName  目标名称
     * @param isExact     是否精确匹配
     * @param result      结果列表
     */
    private static void searchByName(TreeNode currentNode, String targetName, boolean isExact, List<TreeNode> result) {
        // 空值防护:节点为空/目标名称为空,直接返回
        if (currentNode == null || targetName == null || targetName.trim().isEmpty()) {
            return;
        }

        String nodeName = currentNode.getName();
        boolean match = false;
        if (nodeName != null) {
            String target = targetName.trim();
            String nodeNameTrim = nodeName.trim();
            if (isExact) {
                // 精确匹配:trim后完全相等
                match = nodeNameTrim.equals(target);
            } else {
                // 模糊匹配:忽略大小写,包含目标字符串
                match = nodeNameTrim.toLowerCase().contains(target.toLowerCase());
            }
        }

        // 匹配则加入结果
        if (match) {
            result.add(currentNode);
        }

        // 递归遍历子节点
        List<TreeNode> children = currentNode.getChildren();
        if (children != null && !children.isEmpty()) {
            for (TreeNode child : children) {
                searchByName(child, targetName, isExact, result);
            }
        }
    }
}

3. 测试类(TreeNodeTest)

public class TreeNodeTest {
    public static void main(String[] args) {
        // 1. 构建测试树形结构
        TreeNode root = new TreeNode(1L, 0L, "系统管理");
        TreeNode menu1 = new TreeNode(2L, 1L, "用户管理");
        TreeNode menu2 = new TreeNode(3L, 1L, "角色管理");
        TreeNode menu11 = new TreeNode(4L, 2L, "用户列表");
        TreeNode menu12 = new TreeNode(5L, 2L, "新增用户");
        TreeNode menu21 = new TreeNode(6L, 3L, "角色列表");
        TreeNode menu22 = new TreeNode(7L, 3L, "用户角色分配");

        // 组装子节点关系
        menu1.getChildren().add(menu11);
        menu1.getChildren().add(menu12);
        menu2.getChildren().add(menu21);
        menu2.getChildren().add(menu22);
        root.getChildren().add(menu1);
        root.getChildren().add(menu2);

        // 2. 测试1:递归遍历所有节点
        System.out.println("===== 递归遍历所有节点 =====");
        List<TreeNode> allNodes = TreeUtils.listAllNodes(root);
        allNodes.forEach(System.out::println);

        // 3. 测试2:精确匹配名称"用户列表"
        System.out.println("\n===== 精确匹配「用户列表」 =====");
        List<TreeNode> exactMatch = TreeUtils.searchByExactName(root, "用户列表");
        printResult(exactMatch);

        // 4. 测试3:模糊匹配名称"用户"(忽略大小写)
        System.out.println("\n===== 模糊匹配「用户」 =====");
        List<TreeNode> fuzzyMatch = TreeUtils.searchByFuzzyName(root, "用户");
        printResult(fuzzyMatch);

        // 5. 测试4:仅查询"用户管理"子树下的"新增"节点
        System.out.println("\n===== 仅查询「用户管理」子树下模糊匹配「新增」 =====");
        List<TreeNode> partialMatch = TreeUtils.searchByFuzzyName(menu1, "新增");
        printResult(partialMatch);

        // 6. 测试5:匹配不存在的名称
        System.out.println("\n===== 模糊匹配「菜单」 =====");
        List<TreeNode> noMatch = TreeUtils.searchByFuzzyName(root, "菜单");
        printResult(noMatch);
    }

    // 辅助打印方法:统一处理空列表
    private static void printResult(List<TreeNode> nodes) {
        if (nodes.isEmpty()) {
            System.out.println("无匹配节点");
            return;
        }
        nodes.forEach(System.out::println);
    }
}

三、输出结果

===== 递归遍历所有节点 =====
TreeNode{id=1, parentId=0, name='系统管理'}
TreeNode{id=2, parentId=1, name='用户管理'}
TreeNode{id=4, parentId=2, name='用户列表'}
TreeNode{id=5, parentId=2, name='新增用户'}
TreeNode{id=3, parentId=1, name='角色管理'}
TreeNode{id=6, parentId=3, name='角色列表'}
TreeNode{id=7, parentId=3, name='用户角色分配'}

===== 精确匹配「用户列表」 =====
TreeNode{id=4, parentId=2, name='用户列表'}

===== 模糊匹配「用户」 =====
TreeNode{id=2, parentId=1, name='用户管理'}
TreeNode{id=4, parentId=2, name='用户列表'}
TreeNode{id=5, parentId=2, name='新增用户'}
TreeNode{id=7, parentId=3, name='用户角色分配'}

===== 仅查询「用户管理」子树下模糊匹配「新增」 =====
TreeNode{id=5, parentId=2, name='新增用户'}

===== 模糊匹配「菜单」 =====
无匹配节点

四、关键特性说明

  1. 易用性:对外仅暴露 3 个核心方法(listAllNodes/searchByExactName/searchByFuzzyName),调用简单;
  2. 鲁棒性:全链路空值防护,避免NullPointerException
  3. 灵活性:
    • 可遍历整棵树,也可仅遍历某个子树;
    • 支持精确 / 模糊两种匹配规则,满足不同查询场景;
  4. 扩展性:
    • 若需支持 “后序遍历”,只需新增traversePostOrder方法,逻辑为 “先递归子节点,再加入当前节点”;
    • 若需 “找到第一个匹配节点即终止递归”,可修改searchByName为返回boolean,匹配后立即返回true
  5. 性能:递归遍历的时间复杂度为 O (n)(n 为节点总数),是树遍历的最优复杂度;若树层级极深(如超过 1000 层),可替换为迭代方式(栈模拟递归)避免栈溢出。

五、迭代版扩展(防栈溢出)

若树层级极深,可补充迭代版的名称查询方法(以模糊查询为例):
/**
 * 迭代版:模糊查询名称(防栈溢出)
 */
public static List<TreeNode> searchByFuzzyNameIteration(TreeNode rootNode, String targetName) {
    List<TreeNode> result = new ArrayList<>();
    if (rootNode == null || targetName == null || targetName.trim().isEmpty()) {
        return result;
    }

    String target = targetName.trim().toLowerCase();
    Deque<TreeNode> stack = new LinkedList<>();
    stack.push(rootNode);

    while (!stack.isEmpty()) {
        TreeNode node = stack.pop();
        // 匹配逻辑
        String nodeName = node.getName();
        if (nodeName != null && nodeName.trim().toLowerCase().contains(target)) {
            result.add(node);
        }
        // 子节点逆序入栈(保证遍历顺序和递归一致)
        List<TreeNode> children = node.getChildren();
        if (children != null && !children.isEmpty()) {
            for (int i = children.size() - 1; i >= 0; i--) {
                stack.push(children.get(i));
            }
        }
    }
    return result;
}

 

posted @ 2025-12-04 20:01  贩卖长江水  阅读(5)  评论(0)    收藏  举报