由于最近有构建树形列表相关的功能,在此总结下构建方法,也当是做个笔记。 首先需要一个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()));
});
由图可知
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属性。 第一次写这种笔记,如果有更好的方法请告知,发现错误,轻喷谢谢!