Java 树形结构,根据名称搜索

发布于:2025-05-28 ⋅ 阅读:(18) ⋅ 点赞:(0)

一、需求

项目中有个树形列表(三层结构),想根据结构中的名称进行搜索,输出的结果依然保持树形结构。结构如下:
在这里插入图片描述

二、代码

import lombok.Data;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;


@Data
public class ProductCategoryRespVO {


  private Long id;


  private Long parentId;


  private String name;


  private String picUrl;


  private Integer sort;


  private Integer status;


  private String description;


  private LocalDateTime createTime;


  private Integer level;

  // 子分类列表
  private List<ProductCategoryRespVO> children = new ArrayList<>();


  public ProductCategoryRespVO(Long id, Long parentId, String name, String picUrl, Integer sort, Integer status, String description, LocalDateTime createTime, Integer level) {
      this.id = id;
      this.parentId = parentId;
      this.name = name;
      this.picUrl = picUrl;
      this.sort = sort;
      this.status = status;
      this.description = description;
      this.createTime = createTime;
      this.level = level;
      this.children = new ArrayList<>();
  }
  public void addChild(ProductCategoryRespVO child) {
      this.children.add(child);
  }
}



import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;

public class TreeUtil {


   // 搜索上下文类,用于存储搜索路径
   static class SearchContext {
       ProductCategoryRespVO level1Node = null;
       ProductCategoryRespVO level2Node = null;
       ProductCategoryRespVO matchedNode = null;
   }

   // 处理分类数组的搜索方法
   public static List<ProductCategoryRespVO> searchCategoryList(List<ProductCategoryRespVO> categories, String searchName) {
       if (CollectionUtils.isEmpty(categories) || StringUtils.isEmpty(searchName)) {
           return new ArrayList<>();
       }
       List<ProductCategoryRespVO> resultList = new ArrayList<>();

       for (ProductCategoryRespVO root : categories) {
           ProductCategoryRespVO result = searchCategoryTree(root, searchName);
           if (result != null) {
               resultList.add(result);
           }
       }

       return resultList;
   }

   // 搜索单个树的方法
   public static ProductCategoryRespVO searchCategoryTree(ProductCategoryRespVO root, String searchName) {
       SearchContext context = new SearchContext();
       // 查找匹配节点并记录路径
       findMatchedNode(root, searchName, context);

       // 根据匹配节点的层级返回不同的树形结构
       if (context.matchedNode == null) {
           return null; // 未找到匹配节点
       }

       if (context.matchedNode.getLevel() == 1) {
           // 命中level1节点,返回完整节点及其子节点
           return cloneNodeWithChildren(context.matchedNode);
       } else if (context.matchedNode.getLevel() == 2) {
           // 命中level2节点,返回其父节点(level1)和自身及其子节点
           ProductCategoryRespVO level1Clone = cloneNodeWithoutChildren(context.level1Node);
           ProductCategoryRespVO level2Clone = cloneNodeWithChildren(context.matchedNode);
           level1Clone.getChildren().add(level2Clone);
           return level1Clone;
       } else if (context.matchedNode.getLevel() == 3) {
           // 命中level3节点,返回其祖父节点(level1)、父节点(level2)和自身
           ProductCategoryRespVO level1Clone = cloneNodeWithoutChildren(context.level1Node);
           ProductCategoryRespVO level2Clone = cloneNodeWithoutChildren(context.level2Node);
           ProductCategoryRespVO level3Clone = cloneNodeWithChildren(context.matchedNode);

           level2Clone.getChildren().add(level3Clone);
           level1Clone.getChildren().add(level2Clone);
           return level1Clone;
       }

       return null;
   }

   // 递归查找匹配节点并记录路径
   private static void findMatchedNode(ProductCategoryRespVO node, String searchName, SearchContext context) {
       if (node == null) return;

       // 记录路径
       if (node.getLevel() == 1) {
           context.level1Node = node;
           context.level2Node = null; // 重置level2节点,因为进入了新的level1节点
       } else if (node.getLevel() == 2) {
           context.level2Node = node;
       }

//        // 检查当前节点是否匹配
//        if (node.getName().equals(searchName)) {
//            context.matchedNode = node;
//            return;
//        }

       // 检查当前节点是否匹配
       if (node.getName().contains(searchName)) {
           context.matchedNode = node;
           return;
       }

       // 递归搜索子节点
       for (ProductCategoryRespVO child : node.getChildren()) {
           findMatchedNode(child, searchName, context);
           // 如果已经找到匹配节点,直接返回
           if (context.matchedNode != null) {
               return;
           }
       }
   }

   // 克隆节点但不包含子节点
   private static ProductCategoryRespVO cloneNodeWithoutChildren(ProductCategoryRespVO original) {
       ProductCategoryRespVO clone = new ProductCategoryRespVO(
               original.getId(),
               original.getParentId(),
               original.getName(),
               original.getPicUrl(),
               original.getSort(),
               original.getStatus(),
               original.getDescription(),
               original.getCreateTime(),
               original.getLevel()
       );
       return clone;
   }

   // 克隆节点及其所有子节点
   private static ProductCategoryRespVO cloneNodeWithChildren(ProductCategoryRespVO original) {
       ProductCategoryRespVO clone = cloneNodeWithoutChildren(original);
       for (ProductCategoryRespVO child : original.getChildren()) {
           clone.addChild(cloneNodeWithChildren(child));
       }
       return clone;
   }

   // 示例用法
   public static void main(String[] args) {
       // 构建示例树结构 - 女装
       ProductCategoryRespVO womenRoot = new ProductCategoryRespVO(16L, 0L, "女装", "url", 1, 0, null, LocalDateTime.now(), 1);
       ProductCategoryRespVO womenTop = new ProductCategoryRespVO(17L, 16L, "上衣", "url", 1, 0, null, LocalDateTime.now(), 2);
       ProductCategoryRespVO womenVest = new ProductCategoryRespVO(21L, 17L, "背心", "url", 1, 0, null, LocalDateTime.now(), 3);
       ProductCategoryRespVO womenCoat = new ProductCategoryRespVO(23L, 17L, "外套", "url", 1, 0, null, LocalDateTime.now(), 3);

       womenRoot.addChild(womenTop);
       womenTop.addChild(womenVest);
       womenTop.addChild(womenCoat);

       // 构建示例树结构 - 男装
       ProductCategoryRespVO menRoot = new ProductCategoryRespVO(163L, 0L, "男装", "url", 1, 0, null, LocalDateTime.now(), 1);
       ProductCategoryRespVO menTop = new ProductCategoryRespVO(173L, 163L, "上衣", "url", 1, 0, null, LocalDateTime.now(), 2);
       ProductCategoryRespVO menVest = new ProductCategoryRespVO(213L, 173L, "背心", "url", 1, 0, null, LocalDateTime.now(), 3);
       ProductCategoryRespVO menCoat = new ProductCategoryRespVO(233L, 173L, "外套", "url", 1, 0, null, LocalDateTime.now(), 3);

       menRoot.addChild(menTop);
       menTop.addChild(menVest);
       menTop.addChild(menCoat);

       // 创建分类数组
       List<ProductCategoryRespVO> categoryList = new ArrayList<>();
       categoryList.add(womenRoot);
       categoryList.add(menRoot);

       // 搜索示例
       System.out.println("搜索 '女装':");
       List<ProductCategoryRespVO> result1 = searchCategoryList(categoryList, "女装");
       printResultList(result1);

       System.out.println("\n搜索 '上衣':");
       List<ProductCategoryRespVO> result2 = searchCategoryList(categoryList, "上衣");
       printResultList(result2);

       System.out.println("\n搜索 '背心':");
       List<ProductCategoryRespVO> result3 = searchCategoryList(categoryList, "背心");
       printResultList(result3);
   }

   // 辅助方法:打印搜索结果列表
   private static void printResultList(List<ProductCategoryRespVO> resultList) {
       if (resultList.isEmpty()) {
           System.out.println("未找到匹配的分类");
           return;
       }

       for (ProductCategoryRespVO root : resultList) {
           printTree(root, 0);
           System.out.println(); // 空行分隔不同的树
       }
   }

   // 辅助方法:打印树结构
   private static void printTree(ProductCategoryRespVO node, int level) {
       if (node == null) return;

       StringBuilder indent = new StringBuilder();
       for (int i = 0; i < level; i++) {
           indent.append("  ");
       }
       System.out.println(indent + node.getName() + " (Level " + node.getLevel() + ")");

       for (ProductCategoryRespVO child : node.getChildren()) {
           printTree(child, level + 1);
       }
   }

}

三、测试结果

1、搜索“女装”,展示全部,结果如下:
在这里插入图片描述
2、搜索“裙装”,,展示“裙装”的上下节点数据,结果如下:
在这里插入图片描述
3、搜索“T恤”,,展示“T恤”的上下节点数据,结果如下:
在这里插入图片描述