小白如何快速学会构建树形列表

194 阅读2分钟

由于最近有构建树形列表相关的功能,在此总结下构建方法,也当是做个笔记。 首先需要一个Tree实体类

public class TreeModel implements Serializable {
private static final long serialVersionUID = 1L;

private Integer id;

/**
 * 父级id
 */
private Integer pId;

/**
 * 目录名称
 */
private String label;

/**
 * 等级
 */
private String level;

/**
 * 排序
 */
private Integer orderNum;

private List<TreeModel> children=new ArrayList<>();

public Integer getId() {
    return id;
}

public void setId(Integer id) {
    this.id = id;
}

public Integer getpId() {
    return pId;
}

public void setpId(Integer pId) {
    this.pId = pId;
}

public String getLabel() {
    return label;
}

public void setLabel(String label) {
    this.label = label;
}

public String getLevel() {
    return level;
}

public void setLevel(String level) {
    this.level = level;
}

public Integer getOrderNum() {
    return orderNum;
}

public void setOrderNum(Integer orderNum) {
    this.orderNum = orderNum;
}

public List<TreeModel> getChildren() {
    return children;
}

public void setChildren(List<TreeModel> children) {
    this.children = children;
}

方法一 分组

public static List<TreeModel> bulidTree(List<TreeModel> treeList, Integer id){
    Map<Integer,List<TreeModel>> listMap=treeList.stream().filter(node -> !node.getpId().equals(id)).collect(Collectors.groupingBy(TreeModel::getpId));
    treeList.forEach(node -> {
        node.setChildren(listMap.get(node.getId())==null? Collections.emptyList() :listMap.get(node.getId()));
    });
    return treeList.stream().filter(node -> node.getpId().equals(id)).collect(Collectors.toList());
}

第一行把(剔除第一级节点)所有相同父节点的数据分组,以key为父id,value为对应的子节点对象存储在Map里,第二行遍历所有节点设置子节点,第三行返回父节点为0的也就是第一级节点的list。 这里给回答一个疑问,为什么只需要返回第一节点的list,看代码明明是第一级只设入了第二级节点,答案是"引用传递"。 写一个小demo验证一下

   List<Tree> treeList=new ArrayList<>();
                 //Tree(id,父id,目录描述)
		Tree t1=new Tree(1L,0L,"一级节点");
		Tree t2=new Tree(1001L,1L,"二级节点_1");
		Tree t3=new Tree(1002L,1L,"二级节点_2");
		Tree t4=new Tree(1001001L,1001L,"三级节点");
		treeList.add(t1);
		treeList.add(t2);
		treeList.add(t3);
		treeList.add(t4);
		Map<Long,List<Tree>> listMap=treeList.stream().filter(node -> !node.equals(0L)).collect(Collectors.groupingBy(Tree::getpId)); 
		treeList.forEach(node -> {
			node.setChildren(listMap.get(node.getId())==null?new ArrayList<Tree>():listMap.get(node.getId()));
		});
                

image.png 由图可知 treeList中的元素其实指向的是原来的tree对象,当t2发生改变时,t1中的子节点t2也会改变。

方法二 递归法

public static List<TreeModel> bulidTree2(List<TreeModel> treeList,Integer id){
   List<TreeModel> treeList1=new ArrayList<>();
    for (TreeModel tree:treeList
         ) {
        if (id.equals(tree.getpId())) {
            treeList1.add(findChildren(tree,treeList));
        }
    }
    return treeList1;
}
public static TreeModel findChildren(TreeModel tree, List<TreeModel> treeList) {
     for (TreeModel t : treeList) {
        if (tree.getId().equals(t.getpId())) {
            if (tree.getChildren() == null) {
                     tree.setChildren(Collections.emptyList());
            }
            tree.getChildren().add(findChildren(t,treeList));
         }
     }
    return tree;
}

递归法很好理解,从父节点一直递归到叶子节点。 之前处理过组织树下面挂人和部门的混合结构,也是以部门id作为key,value为节点对象存储,由第一级节点递归查询。

个人推荐第一种写法。经本地测试所有节点2500个,第一种耗时17ms,第二种耗时247ms。但是需注意第一种写法,在设置子节点时,如果获取不到子节点需判null,本文使用三目表达式规避,如果不判断,这返回数据没有子节点,就没有children属性。 第一次写这种笔记,如果有更好的方法请告知,发现错误,轻喷谢谢!