一、需求
项目中有个树形列表(三层结构),想根据结构中的名称进行搜索,输出的结果依然保持树形结构。结构如下:
二、代码
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恤”的上下节点数据,结果如下: