咱们继续学Java——高级篇 第一百七十二篇:之Java高级Swing编程之树组件的节点遍历与应用

在Java编程的学习道路上,我们始终携手共进,不断探索知识的深度。今天,我们将深入学习Java核心技术卷II中高级Swing编程关于树组件节点遍历与应用的部分,帮助大家掌握如何遍历树中的节点,以及如何将节点遍历应用到实际场景中,提升对树组件的操作和运用能力。

一、节点遍历的方法与顺序

(一)广度优先遍历(Breadth-First Traversal)

  1. 遍历方式:广度优先遍历是一种层次化的遍历方式,它从根节点开始,先访问根节点,然后依次访问根节点的所有子节点,接着访问这些子节点的所有子节点,依此类推。这种遍历方式就像一层一层地剥开洋葱,先处理离根节点最近的节点,再逐渐向外扩展。例如,在一个表示组织结构的树中,广度优先遍历会先列出公司的高层领导(根节点),然后是各个部门的负责人(根节点的子节点),再是每个部门下的小组组长(子节点的子节点)等。

  2. 实现方法与示例:在DefaultMutableTreeNode类中,可以使用breadthFirstEnumeration方法进行广度优先遍历,该方法返回一个枚举对象。通过迭代这个枚举对象,可以访问树中的每个节点。以下是一个简单的代码示例,展示如何使用广度优先遍历打印树中所有节点的用户对象:

    import javax.swing.tree.DefaultMutableTreeNode;
    import javax.swing.tree.TreeNode;
    import java.util.Enumeration;
    public class TreeBreadthFirstTraversalExample {
    public static void main(String[] args) {
    // 创建一个简单的树结构
    DefaultMutableTreeNode root = new DefaultMutableTreeNode("Root");
    DefaultMutableTreeNode child1 = new DefaultMutableTreeNode("Child 1");
    root.add(child1);
    DefaultMutableTreeNode child2 = new DefaultMutableTreeNode("Child 2");
    root.add(child2);
    DefaultMutableTreeNode grandChild1 = new DefaultMutableTreeNode("Grand Child 1");
    child1.add(grandChild1);
    // 进行广度优先遍历
    Enumeration<TreeNode> breadthFirst = root.breadthFirstEnumeration();
    while (breadthFirst.hasMoreElements()) {
    TreeNode node = breadthFirst.nextElement();
    System.out.println(node.getUserObject());
    }
    }
    }
    

    在这个示例中,我们创建了一个包含根节点、两个子节点和一个孙节点的简单树结构。然后,使用breadthFirstEnumeration方法获取枚举对象,并通过循环打印出每个节点的用户对象。运行结果将按照广度优先的顺序依次打印出“Root”、“Child 1”、“Child 2”、“Grand Child 1”。

    (二)深度优先遍历(Depth-First Traversal)

  3. 遍历方式:深度优先遍历有两种常见的方式,即后序遍历(Postorder Traversal)和先序遍历(Preorder Traversal)。后序遍历就像一只老鼠陷入树状陷阱,它沿着第一条路径迅速爬行,直到到达一个叶节点位置,然后原路返回并转入下一条路径,依此类推。整个查找过程是先访问到子节点,然后才访问到父节点。先序遍历则首先枚举父节点,然后是子节点。例如,在一个文件系统目录树中,后序遍历可能会先遍历到最深层的文件,然后再依次返回上级目录;而先序遍历会先列出根目录,再依次列出每个子目录及其文件。

  4. 实现方法与示例

    • 后序遍历(Postorder Traversal):在DefaultMutableTreeNode类中,depthFirstEnumeration方法(等同于postorderTraversal方法)可以实现后序遍历。以下是一个使用后序遍历计算树中所有节点值之和的示例(假设节点的用户对象是整数):

      import javax.swing.tree.DefaultMutableTreeNode;
      import javax.swing.tree.TreeNode;
      import java.util.Enumeration;
      public class TreePostorderTraversalExample {
      public static void main(String[] args) {
      // 创建一个包含整数节点值的树结构
      DefaultMutableTreeNode root = new DefaultMutableTreeNode(10);
      DefaultMutableTreeNode child1 = new DefaultMutableTreeNode(5);
      root.add(child1);
      DefaultMutableTreeNode child2 = new DefaultMutableTreeNode(3);
      root.add(child2);
      DefaultMutableTreeNode grandChild1 = new DefaultMutableTreeNode(2);
      child1.add(grandChild1);
      int sum = postorderTraversalSum(root);
      System.out.println("树中所有节点值之和为:" + sum);
      }
      private static int postorderTraversalSum(DefaultMutableTreeNode node) {
      int sum = 0;
      Enumeration<TreeNode> postorder = node.depthFirstEnumeration();
      while (postorder.hasMoreElements()) {
      TreeNode currentNode = postorder.nextElement();
      sum += (int) currentNode.getUserObject();
      }
      return sum;
      }
      }
      

      在这个示例中,我们创建了一个简单的树结构,节点的用户对象为整数。通过后序遍历计算所有节点值之和,先计算子节点的值,再计算父节点的值,最终得到树中所有节点值的总和。

    • 先序遍历(Preorder Traversal):虽然文档中未提及先序遍历的具体方法,但我们可以根据深度优先遍历的原理自行实现。以下是一个简单的先序遍历示例,用于打印树中节点的层次结构(通过缩进表示层次):

      import javax.swing.tree.DefaultMutableTreeNode;
      import javax.swing.tree.TreeNode;
      import java.util.Enumeration;
      public class TreePreorderTraversalExample {
      public static void main(String[] args) {
      // 创建一个简单的树结构
      DefaultMutableTreeNode root = new DefaultMutableTreeNode("Root");
      DefaultMutableTreeNode child1 = new DefaultMutableTreeNode("Child 1");
      root.add(child1);
      DefaultMutableTreeNode child2 = new DefaultMutableTreeNode("Child 2");
      root.add(child2);
      DefaultMutableTreeNode grandChild1 = new DefaultMutableTreeNode("Grand Child 1");
      child1.add(grandChild1);
      preorderTraversalPrint(root, 0);
      }
      private static void preorderTraversalPrint(DefaultMutableTreeNode node, int level) {
      for (int i = 0; i < level; i++) {
      System.out.print(" ");
      }
      System.out.println(node.getUserObject());
      Enumeration<TreeNode> children = node.children();
      while (children.hasMoreElements()) {
      TreeNode child = children.nextElement();
      preorderTraversalPrint((DefaultMutableTreeNode) child, level + 1);
      }
      }
      }
      

      在这个示例中,我们创建了一个树结构,并使用先序遍历打印节点的层次结构。通过递归调用preorderTraversalPrint方法,先打印父节点,再递归打印子节点,并根据层次进行缩进,使输出结果更清晰地展示树的结构。

      (三)路径查找遍历(Path From Ancestor Enumeration)

  5. 功能与用途pathFromAncestorEnumeration方法用于查找一条从祖先节点到给定节点之间的路径,然后枚举出该路径中的所有节点。这在需要获取两个节点之间的路径信息或判断两个节点是否存在祖孙关系等场景中非常有用。例如,在一个权限管理系统中,如果要判断一个用户是否具有访问某个资源的权限,可以通过查找从根节点(代表最高权限)到该资源节点的路径,检查路径上的节点是否满足权限要求。

  6. 示例代码(假设场景):以下是一个简单的示例,假设我们有一个树结构表示一个公司的部门层级关系,我们要查找从根节点到某个特定部门节点的路径并打印出来:

    import javax.swing.tree.DefaultMutableTreeNode;
    import javax.swing.tree.TreeNode;
    import java.util.Enumeration;
    public class TreePathFromAncestorExample {
    public static void main(String[] args) {
    // 创建一个公司部门层级树结构
    DefaultMutableTreeNode root = new DefaultMutableTreeNode("公司总部");
    DefaultMutableTreeNode department1 = new DefaultMutableTreeNode("研发部");
    root.add(department1);
    DefaultMutableTreeNode subDepartment1 = new DefaultMutableTreeNode("软件研发组");
    department1.add(subDepartment1);
    DefaultMutableTreeNode subDepartment2 = new DefaultMutableTreeNode("硬件研发组");
    department1.add(subDepartment2);
    DefaultMutableTreeNode department2 = new DefaultMutableTreeNode("市场部");
    root.add(department2);
    // 查找从根节点到“软件研发组”节点的路径并打印
    DefaultMutableTreeNode targetNode = subDepartment1;
    Enumeration<TreeNode> path = root.pathFromAncestorEnumeration(targetNode);
    while (path.hasMoreElements()) {
    TreeNode node = path.nextElement();
    System.out.println(node.getUserObject());
    }
    }
    }
    

    在这个示例中,我们创建了一个公司部门层级树结构,然后使用pathFromAncestorEnumeration方法查找从根节点“公司总部”到“软件研发组”节点的路径,并将路径上的节点依次打印出来。通过这个示例,我们可以看到如何使用该方法获取树中两个节点之间的路径信息。

    二、节点遍历的应用场景

    (一)构建继承树展示类关系

  7. 需求分析:在实际编程中,经常需要展示类之间的继承关系,树组件是一种很好的展示方式。通过节点遍历,我们可以从根类(如java.lang.Object)开始,查找所有子类,并将它们按照继承关系构建成一棵树。例如,在一个Java代码分析工具中,我们可以使用树组件展示一个项目中所有类的继承关系,方便开发人员查看和理解类的层次结构。

  8. 实现思路与示例代码:以下是一个简单的示例,模拟构建一个类继承树(为了简化,只包含部分常见类):

    import javax.swing.JFrame;
    import javax.swing.JScrollPane;
    import javax.swing.JTree;
    import javax.swing.tree.DefaultMutableTreeNode;
    import javax.swing.tree.TreeNode;
    import java.util.Enumeration;
    public class ClassInheritanceTreeExample {
    public static void main(String[] args) {
    JFrame frame = new JFrame("类继承树示例");
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    // 创建根节点(以Object类为根)
    DefaultMutableTreeNode root = new DefaultMutableTreeNode("java.lang.Object");
    // 添加一些常见类及其继承关系
    DefaultMutableTreeNode componentNode = new DefaultMutableTreeNode("java.awt.Component");
    root.add(componentNode);
    DefaultMutableTreeNode containerNode = new DefaultMutableTreeNode("java.awt.Container");
    componentNode.add(containerNode);
    DefaultMutableTreeNode windowNode = new DefaultMutableTreeNode("java.awt.Window");
    containerNode.add(windowNode);
    DefaultMutableTreeNode frameNode = new DefaultMutableTreeNode("java.awt.Frame");
    windowNode.add(frameNode);
    DefaultMutableTreeNode jFrameNode = new DefaultMutableTreeNode("javax.swing.JFrame");
    frameNode.add(jFrameNode);
    DefaultMutableTreeNode collectionNode = new DefaultMutableTreeNode("java.util.Collection");
    root.add(collectionNode);
    DefaultMutableTreeNode abstractListNode = new DefaultMutableTreeNode("java.util.AbstractList");
    collectionNode.add(abstractListNode);
    DefaultMutableTreeNode arrayListNode = new DefaultMutableTreeNode("java.util.ArrayList");
    abstractListNode.add(arrayListNode);
    // 创建树并添加到滚动面板
    JTree tree = new JTree(root);
    JScrollPane scrollPane = new JScrollPane(tree);
    frame.add(scrollPane);
    // 设置窗口大小并显示
    frame.setSize(400, 300);
    frame.setVisible(true);
    }
    }
    

    在这个示例中,我们手动创建了一个简单的类继承树结构,包含了一些常见的Java类及其继承关系。在实际应用中,可以通过反射机制自动查找项目中的所有类,并根据它们的继承关系构建完整的继承树。通过这个示例,我们可以看到如何利用树组件和节点遍历的概念来展示类之间的继承关系。

    (二)权限管理系统中的权限判断(假设场景)

  9. 需求分析:在权限管理系统中,权限通常是按照层级结构组织的,类似于树状结构。例如,系统可能有管理员权限(根节点),管理员下可能有不同模块的管理权限(子节点),每个模块管理权限下又有具体的操作权限(孙节点等)。通过节点遍历,我们可以根据用户拥有的权限节点,查找从根节点到该权限节点的路径,然后根据路径上的节点信息判断用户是否具有访问某个资源或执行某个操作的权限。

  10. 实现思路与示例代码(简化版):以下是一个简单的权限判断示例,假设我们有一个简单的权限树结构,用户具有某个权限节点,我们要判断用户是否具有访问特定资源的权限(这里简化为判断是否具有某个特定权限节点):

    import javax.swing.tree.DefaultMutableTreeNode;
    import javax.swing.tree.TreeNode;
    import java.util.Enumeration;
    public class PermissionTreeExample {
    public static void main(String[] args) {
    // 创建权限树结构
    DefaultMutableTreeNode root = new DefaultMutableTreeNode("管理员权限");
    DefaultMutableTreeNode module1Permission = new DefaultMutableTreeNode("用户管理权限");
    root.add(module1Permission);
    DefaultMutableTreeNode operation1Permission = new DefaultMutableTreeNode("查看用户信息权限");
    module1Permission.add(operation1Permission);
    DefaultMutableTreeNode operation2Permission = new DefaultMutableTreeNode("修改用户信息权限");
    module1Permission.add(operation2Permission);
    // 假设用户具有“修改用户信息权限”
    DefaultMutableTreeNode userPermission = operation2Permission;
    boolean hasPermission = hasPermission(root, userPermission);
    if (hasPermission) {
    System.out.println("用户具有访问该资源的权限。");
    } else {
    System.out.println("用户不具有访问该资源的权限。");
    }
    }
    private static boolean hasPermission(DefaultMutableTreeNode root, DefaultMutableTreeNode targetPermission) {
    Enumeration<TreeNode> path = root.pathFromAncestorEnumeration(targetPermission);
    while (path.hasMoreElements()) {
    TreeNode node = path.nextElement();
    if (node == targetPermission) {
    return true;
    }
    }
    return false;
    }
    }
    

    在这个示例中,我们创建了一个简单的权限树结构,然后假设用户具有“修改用户信息权限”节点。通过pathFromAncestorEnumeration方法查找从根节点到用户权限节点的路径,并检查路径中是否包含目标权限节点,从而判断用户是否具有访问该资源的权限。在实际的权限管理系统中,权限结构会更加复杂,可能涉及到多个层级和多种权限类型,但基本原理是相似的。
    通过对Java高级Swing编程中树组件节点遍历与应用的学习,我们掌握了强大的树节点操作工具,能够在各种实际场景中灵活运用树组件,提升应用程序的功能和用户体验。希望这篇博客能帮助大家在Java界面编程领域取得更大的进步。如果大家在阅读过程中有任何疑问或建议,欢迎在评论区留言。如果觉得这篇博客对你有帮助,别忘了点赞、收藏和关注我的博客,你们的支持是我继续分享知识的最大动力!让我们一起在Java编程的道路上不断探索,攻克更多的技术难题!

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐