如何在Java中验证列表对象的父子关系及顺序合法性
问题说明
需要对外部服务返回的Category列表执行两项校验:
- 父子关系合法性:根节点的
parentId为null;非根节点的parentId必须指向列表中存在的分类,且不存在循环引用。 - 顺序合法性:列表必须严格遵循层级递进顺序,即每个非根节点必须紧跟在其父节点之后(如示例中的
HW → Phones → IPhone → 13 Pro),若顺序不符合要求则抛出异常。
待优化代码
List<Category> categoryList = getCategoryList(); categoryList.stream().forEach(category -> { if (category.getParentId().isEmpty()) { String rootCategory = category.getId(); if (category.getParentId().equals(rootCategory)) { String category2 = category.getId(); if (category.getParentId().equals(category2)) { String category3 = category.getId(); if (category.getParentId().equals(category3)) { String category4 = category.getId(); } } } } });
优化后的实现方案
核心思路
- 构建分类ID到对象的映射表,快速校验父节点是否存在;
- 用栈维护当前的层级路径,确保每个节点的父节点是路径的最后一个节点(保证顺序合规);
- 分步校验父子关系合法性与顺序合法性,不符合时抛出自定义异常。
完整代码实现
import java.util.*; // 自定义异常类,用于抛出校验错误 class CategoryValidationException extends RuntimeException { public CategoryValidationException(String message) { super(message); } } // Category类示例(根据实际结构调整) class Category { private String categoryId; private String parentId; public Category(String categoryId, String parentId) { this.categoryId = categoryId; this.parentId = parentId; } public String getCategoryId() { return categoryId; } public String getParentId() { return parentId; } } public class CategoryValidator { public static void validateCategoryList(List<Category> categoryList) { if (categoryList == null || categoryList.isEmpty()) { throw new CategoryValidationException("分类列表不能为空"); } // 构建ID到Category的映射,用于快速查找父节点 Map<String, Category> categoryMap = new HashMap<>(); for (Category category : categoryList) { categoryMap.put(category.getCategoryId(), category); } // 用栈维护当前层级路径,栈顶为当前层级的父节点 Deque<Category> hierarchyStack = new ArrayDeque<>(); for (Category current : categoryList) { String currentId = current.getCategoryId(); String parentId = current.getParentId(); // 校验父子关系合法性 if (parentId == null) { // 根节点:栈必须为空(只能有一个根节点,且是第一个节点) if (!hierarchyStack.isEmpty()) { throw new CategoryValidationException("根节点必须是列表第一个节点,且只能有一个根节点"); } hierarchyStack.push(current); } else { // 非根节点:父节点必须存在于映射中 Category parent = categoryMap.get(parentId); if (parent == null) { throw new CategoryValidationException("分类[" + currentId + "]的父节点[" + parentId + "]不存在"); } // 校验循环引用(父节点的父节点不能指向当前节点,或更上层循环) if (isCircularReference(current, parent, categoryMap)) { throw new CategoryValidationException("分类[" + currentId + "]存在循环引用"); } // 校验顺序合法性:当前节点的父节点必须是栈顶节点(紧跟父节点之后) if (!hierarchyStack.isEmpty() && !hierarchyStack.peek().getCategoryId().equals(parentId)) { throw new CategoryValidationException("分类[" + currentId + "]顺序错误,必须紧跟在父节点[" + parentId + "]之后"); } // 入栈,进入下一层级 hierarchyStack.push(current); } } } // 辅助方法:检查是否存在循环引用 private static boolean isCircularReference(Category current, Category parent, Map<String, Category> categoryMap) { Set<String> visited = new HashSet<>(); visited.add(current.getCategoryId()); Category temp = parent; while (temp != null) { String tempId = temp.getCategoryId(); if (visited.contains(tempId)) { return true; } visited.add(tempId); temp = categoryMap.get(temp.getParentId()); } return false; } // 测试示例 public static void main(String[] args) { // 合法顺序的列表 List<Category> validList = Arrays.asList( new Category("HW", null), new Category("Phones", "HW"), new Category("IPhone", "Phones"), new Category("13 Pro", "IPhone") ); validateCategoryList(validList); System.out.println("合法列表校验通过"); // 非法顺序的列表 List<Category> invalidList = Arrays.asList( new Category("HW", null), new Category("13 Pro", "IPhone"), new Category("IPhone", "Phones"), new Category("Phones", "HW") ); try { validateCategoryList(invalidList); } catch (CategoryValidationException e) { System.out.println("非法列表校验失败:" + e.getMessage()); } } }
代码说明
- 映射表:
categoryMap用于快速查找父节点是否存在,避免重复遍历列表; - 层级栈:
hierarchyStack记录当前的层级路径,确保每个非根节点紧跟其父节点; - 循环引用校验:
isCircularReference方法通过遍历父节点链,检查是否存在循环; - 异常处理:自定义
CategoryValidationException明确抛出校验错误信息,便于定位问题。
内容的提问来源于stack exchange,提问作者v_1403
相关产品推荐
相关产品推荐

