现在有一个树节点对象TreeNode其代码为:
@Data
public class TreeNode {
String id;
String pid;
String name;
List<TreeNode> children;
}
现在问题为:如何快速将查询出来的List<TreeNode> vos对象转换为树形结构?
笔者之前的思路为:
1、从底向顶,即:找出叶子节点,将叶子节点加入children。
1.0、用vos构建key为id,value为treeNode的Map对象idMap;
1.1、遍历idMap的values收集当前所有pid,得到set集合pidSet;
1.2、再次遍历idMap,如果当前节点的id不在pidSet中,且当前节点有父节点,则从idMap中删除当前节点,将当前节点加入其父节点的children中
1.3、重复1.1-1.2的步骤,直到再也没有可删除的节点为止
2、从顶向底,即找出根节点,为根节点找出其子节点
2.0、用vos构建key为id,value为treeNoe的map对象idMap;
2.1、遍历idMap,找出每个treeNode的上级节点,再为其上级节点找其上级节点,直到找到该treenode的根节点,将根节点id放到set集合rootSet中。
2.2、遍历rootSet,用id获取当前节点,为其找到其子节点
2.3、递归遍历子节点,为其找到其对应的下一级节点
上面2种算法虽然能实现list转tree的功能,但是循环遍历多次,算法复杂,一不小心就能出错。笔者并不推荐。
笔者最新的算法依据为:
1、treeNode对象为Obejct复杂对象,所以在内存中是使用的引用;
2、treeNode对象的children是List对象,所以它在内中使用的是引用;
3、只要找到所有treeNode与其children的引用关系并进行设置;
4、找出根节点即完成了list转tree。
具体代码如下:
public static List<TreeNode> list2Tree(List<TreeNode> vos){
List<TreeNode> result=new ArrayList<>();
if(!ObjectUtils.isEmpty(vos)){
Map<String, TreeNode> idMap = vos.stream().collect(Collectors.toMap(vo -> vo.getId(), vo -> vo));
Map<String, List<TreeNode>> parentMap = vos.stream().collect(Collectors.groupingBy(vo -> vo.getPid()));
Set<String> findParents=new HashSet<>();
for(String id:parentMap.keySet()){
if(id!=null&&idMap.containsKey(id)){
TreeNode treeNode = idMap.get(id);
List<TreeNode> treeNodes = parentMap.get(id);
//此处也需要考虑为treeNodes排序,当然如果vos已经是有序的,则不用
treeNodes .sort(Comparator.comparing(TreeNode::getId));
treeNode.setChildren(treeNodes);
findParents.addAll(treeNodes.stream().map(vo->vo.getId()).collect(Collectors.toSet()));
}
}
for(String id:idMap.keySet()){
if(!findParents.contains(id)){
result.add(idMap.get(id));
}
}
}
//注意:map是乱序的,所以此处必须排序
result.sort(Comparator.comparing(TreeNode::getId));
return result;
}
ps1:发现了一个bug:idMap应该是hashmap,所以在返回结果前进行排序!
ps2:排序的Comparator也是个坑!排序使用的字段不能有null值存在!