如何使用Java 8将父子对转换为层级树形结构?
使用Java 8流式API简化父子节点转树形结构
当然可以借助Java 8的流式特性来大幅简化树形结构的构建!你的手动循环方式虽然可行,但流式API能让代码更简洁、更具声明式风格,减少冗余的条件判断和重复逻辑。
首先先修正一下原代码里的几个小问题(不然运行会出问题):
Pair类里的getChildId()方法返回的是未赋值的childId字段,实际应该返回childCode(因为构造函数里传入的是childCode),建议把方法名改成对应字段的 getter,避免混淆。MegaMenuDTO类里的构造函数初始化了parentId,但类中并没有定义这个字段,应该改成parentCode,和类里的get/setParentCode()对应。- 原代码中根节点判断是
parentId.equals("null"),但输入里的根节点父值是NULL(大写),要注意大小写统一,或者处理成兼容大小写的判断逻辑。
接下来是基于Java 8的实现方案:
完整代码实现
修正后的Pair类
public class Pair { private String childCode; private String parentCode; public Pair(String childCode, String parentCode) { this.childCode = childCode; this.parentCode = parentCode; } public String getChildCode() { return childCode; } public String getParentCode() { return parentCode; } }
修正后的MegaMenuDTO类
import java.util.ArrayList; import java.util.List; public class MegaMenuDTO { private String id; private String name; private String parentCode; private String title; private List<MegaMenuDTO> childrenItems; public MegaMenuDTO() { this.id = ""; this.name = ""; this.parentCode = ""; this.childrenItems = new ArrayList<>(); } // 新增带id的构造函数,方便流式创建节点 public MegaMenuDTO(String id) { this(); this.id = id; } public String getId() { return id; } public void setId(String id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getParentCode() { return parentCode; } public void setParentCode(String parentCode) { this.parentCode = parentCode; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public List<MegaMenuDTO> getChildrenItems() { return childrenItems; } public void setChildrenItems(List<MegaMenuDTO> childrenItems) { this.childrenItems = childrenItems; } public void addChildrenItem(MegaMenuDTO childrenItem) { if (!this.childrenItems.contains(childrenItem)) { this.childrenItems.add(childrenItem); } } @Override public String toString() { return "MegaMenuDTO [id=" + id + ", parentCode=" + parentCode + ", childrenItems=" + childrenItems + "]"; } }
Java 8风格的树形构建主类
import java.util.*; import java.util.stream.Collectors; import java.util.stream.Stream; public class TreeBuilder { public static void main(String[] args) { // 构建输入的父子节点对 List<Pair> pairs = Arrays.asList( new Pair("H", "G"), new Pair("F", "G"), new Pair("G", "D"), new Pair("E", "D"), new Pair("A", "E"), new Pair("B", "C"), new Pair("C", "E"), new Pair("D", "NULL"), new Pair("Z", "Y"), new Pair("Y", "X"), new Pair("X", "NULL") ); // 1. 将所有唯一节点转换为MegaMenuDTO并缓存到Map中 Map<String, MegaMenuDTO> nodeMap = pairs.stream() // 提取所有子节点和父节点ID,去重后得到所有节点ID .flatMap(pair -> Stream.of(pair.getChildCode(), pair.getParentCode())) .distinct() // 过滤掉代表根节点父项的NULL .filter(id -> !"NULL".equals(id)) .collect(Collectors.toMap( id -> id, MegaMenuDTO::new, // 重复ID时保留已有对象(理论上不会出现) (existing, replacement) -> existing )); // 2. 按父节点ID分组所有子节点 Map<String, List<MegaMenuDTO>> childrenByParent = pairs.stream() .filter(pair -> !"NULL".equals(pair.getParentCode())) .collect(Collectors.groupingBy( Pair::getParentCode, Collectors.mapping( pair -> nodeMap.get(pair.getChildCode()), Collectors.toList() ) )); // 3. 关联父子节点关系 childrenByParent.forEach((parentId, children) -> { MegaMenuDTO parent = nodeMap.get(parentId); if (parent != null) { parent.setChildrenItems(children); // 为子节点设置父节点编码 children.forEach(child -> child.setParentCode(parentId)); } }); // 4. 提取所有根节点(父节点为NULL的节点) List<MegaMenuDTO> rootNodes = pairs.stream() .filter(pair -> "NULL".equals(pair.getParentCode())) .map(pair -> nodeMap.get(pair.getChildCode())) .distinct() .collect(Collectors.toList()); // 打印树形结构 System.out.println("生成的层级树形结构:"); rootNodes.forEach(root -> printTree(root, 0)); } // 递归打印树形结构的工具方法 private static void printTree(MegaMenuDTO node, int depth) { // 打印缩进前缀 if (depth > 0) { System.out.print("│ ".repeat(depth - 1) + "└──"); } // 打印当前节点ID System.out.println(node.getId()); // 递归打印子节点 node.getChildrenItems().forEach(child -> printTree(child, depth + 1)); } }
代码说明
- 节点缓存Map:通过
flatMap一次性提取所有唯一节点ID,批量创建MegaMenuDTO对象并缓存,比原代码的循环判断更高效简洁。 - 子节点分组:使用
Collectors.groupingBy按父节点ID快速聚合子节点,省去了原代码中手动添加子节点的重复逻辑。 - 关联父子关系:通过
forEach遍历分组结果,直接为父节点绑定子节点列表,同时完善子节点的父编码属性。 - 根节点提取:流式过滤出父节点为
NULL的节点,得到最终的树形根节点集合。
这个实现利用Java 8的流式API将数据处理逻辑声明化,减少了手动循环的冗余代码,可读性和维护性都比原方案更好。
内容的提问来源于stack exchange,提问作者Soumya
相关产品推荐
相关产品推荐

