递归在树结构中经常用到,好处就是代码简洁,不需要写大量的代码,还有就是不怕树的层级加深,拥抱变化。for循环易于理解,但是需要写大量的代码,还有就是对于需求不确定的话,即存在可能未来加深树的层级,就要改动代码了,违背了迪米法则。
下面的做法也经常遇到,方式1没有在表里加上level等级字段,方式2在表里加上level等级字段,方式3没有在表里加上level等级字段,也发现了一些区别。在数据不是很多情况下,效果略微有点不一样。有些情况下,当然最好的做法是按需加载。
@GetMapping
public TagTree list() {
TagTree root = new TagTree();
root.setTagId(0L);
root.setTagName("ceam");
List<SystemTagDef> list = this.tagDefJpa.findAll();
long l1 = System.currentTimeMillis();
buildTree(list);
long l2 = System.currentTimeMillis();
System.out.println(">>>>>>>>>>>>>>>>>>>>>>>1="+(l2-l1));
buildTree2(list);
long l3 = System.currentTimeMillis();
System.out.println(">>>>>>>>>>>>>>>>>>>>>>>2="+(l3-l2));
this.child(root, list);
long l4 = System.currentTimeMillis();
System.out.println(">>>>>>>>>>>>>>>>>>>>>>>3="+(l4-l3));
return root;
}
private List<SystemTagDefVO> buildTree(List<SystemTagDef> list) {
List<SystemTagDefVO> systemTagDefVOList = new ArrayList<>();
list.forEach(item -> {
SystemTagDefVO systemTagDefVO = new SystemTagDefVO();
systemTagDefVO.setTagId(item.getTagId());
systemTagDefVO.setParentId(item.getParentId());
systemTagDefVO.setTagName(item.getTagName());
systemTagDefVOList.add(systemTagDefVO);
});
// 取出所有一级的
List<SystemTagDefVO> collect = systemTagDefVOList.stream()
.filter(item -> item.getParentId().toString().contains("0"))
.collect(Collectors.toList());
systemTagDefVOList.removeAll(collect);
// 为一级添加二级
collect.forEach(item -> {
item.setList(getToo(systemTagDefVOList, item.getTagId().toString()));
systemTagDefVOList.removeAll(item.getList());
// 为二级添加三级
item.getList().forEach(too -> {
// 移除一级,移除部分二级,最好移除所有二级
too.setList(getToo(systemTagDefVOList, too.getTagId().toString()));
// 为三级添加四级
systemTagDefVOList.removeAll(too.getList());
too.getList().forEach(three -> {
three.setList(getToo(systemTagDefVOList, three.getTagId().toString()));
systemTagDefVOList.removeAll(three.getList());
});
});
});
return collect;
}
private List<SystemTagDefVO> buildTree2(List<SystemTagDef> list) {
List<SystemTagDefVO> systemTagDefVOS = new ArrayList<>();
list.forEach(item -> {
SystemTagDefVO systemTagDefVO = new SystemTagDefVO();
systemTagDefVO.setTagId(item.getTagId());
systemTagDefVO.setParentId(item.getParentId());
systemTagDefVO.setTagName(item.getTagName());
systemTagDefVO.setLevel(item.getLevel());
systemTagDefVOS.add(systemTagDefVO);
});
// 取出所有1级的
List<SystemTagDefVO> collect = systemTagDefVOS.stream()
.filter(item -> item.getParentId().toString().contains("0"))
.collect(Collectors.toList());
systemTagDefVOS.removeAll(collect);
// 取出所有2级的
List<SystemTagDefVO> c2 = systemTagDefVOS.stream()
.filter(item -> item.getLevel().toString().contains("2"))
.collect(Collectors.toList());
systemTagDefVOS.removeAll(c2);
// 取出所有3级的
List<SystemTagDefVO> c3 = systemTagDefVOS.stream()
.filter(item -> item.getLevel().toString().contains("3"))
.collect(Collectors.toList());
systemTagDefVOS.removeAll(c3);
// 取出所有4级的
List<SystemTagDefVO> c4 = systemTagDefVOS.stream()
.filter(item -> item.getLevel().toString().contains("4"))
.collect(Collectors.toList());
systemTagDefVOS.removeAll(c4);
//
collect.forEach(item -> {
item.setList(getToo(c2, item.getTagId().toString()));
c2.removeAll(item.getList());
item.getList().forEach(too -> {
too.setList(getToo(c3, too.getTagId().toString()));
c3.removeAll(too.getList());
too.getList().forEach(three -> {
three.setList(getToo(c4, three.getTagId().toString()));
c4.removeAll(three.getList());
});
});
});
return collect;
}
private List<SystemTagDefVO> getToo(List<SystemTagDefVO> withOutFirstList, String parentId) {
List<SystemTagDefVO> collect = withOutFirstList.stream()
.filter(item -> item.getParentId().toString().contains(parentId))
.collect(Collectors.toList());
return collect;
}
private void child(TagTree root, List<SystemTagDef> allTags) {
// 从allTags中取出对应子标签
List<SystemTagDef> childs = allTags.stream().filter(item ->
root.getTagId().equals(item.getParentId())).collect(Collectors.toList());
allTags.removeAll(childs);
// 注意forEach本身就是有终止条件的,即遍历到最后一个元素
childs.forEach(item -> {
TagTree tag = new TagTree();
tag.setTagId(item.getTagId());
tag.setTagName(item.getTagName());
tag.setParentId(item.getParentId());
// 递归调用
this.child(tag , allTags);
root.appendChildren(tag);
});
}
表的数据在1000左右
3者的时间如下,都是几毫秒,没有差多少
Hibernate: select systemtagd0_.tag_id as tag_id1_6_, systemtagd0_.crt_time as crt_time2_6_, systemtagd0_.level as level3_6_, systemtagd0_.parent_id as parent_i4_6_, systemtagd0_.tag_desc as tag_desc5_6_, systemtagd0_.tag_name as tag_name6_6_ from tb_tag_def systemtagd0_
>>>>>>>>>>>>>>>>>>>>>>>1=4
>>>>>>>>>>>>>>>>>>>>>>>2=2
>>>>>>>>>>>>>>>>>>>>>>>3=3
Hibernate: select systemtagd0_.tag_id as tag_id1_6_, systemtagd0_.crt_time as crt_time2_6_, systemtagd0_.level as level3_6_, systemtagd0_.parent_id as parent_i4_6_, systemtagd0_.tag_desc as tag_desc5_6_, systemtagd0_.tag_name as tag_name6_6_ from tb_tag_def systemtagd0_
>>>>>>>>>>>>>>>>>>>>>>>1=5
>>>>>>>>>>>>>>>>>>>>>>>2=2
>>>>>>>>>>>>>>>>>>>>>>>3=3
Hibernate: select systemtagd0_.tag_id as tag_id1_6_, systemtagd0_.crt_time as crt_time2_6_, systemtagd0_.level as level3_6_, systemtagd0_.parent_id as parent_i4_6_, systemtagd0_.tag_desc as tag_desc5_6_, systemtagd0_.tag_name as tag_name6_6_ from tb_tag_def systemtagd0_
>>>>>>>>>>>>>>>>>>>>>>>1=5
>>>>>>>>>>>>>>>>>>>>>>>2=2
>>>>>>>>>>>>>>>>>>>>>>>3=5
Hibernate: select systemtagd0_.tag_id as tag_id1_6_, systemtagd0_.crt_time as crt_time2_6_, systemtagd0_.level as level3_6_, systemtagd0_.parent_id as parent_i4_6_, systemtagd0_.tag_desc as tag_desc5_6_, systemtagd0_.tag_name as tag_name6_6_ from tb_tag_def systemtagd0_
>>>>>>>>>>>>>>>>>>>>>>>1=6
>>>>>>>>>>>>>>>>>>>>>>>2=2
>>>>>>>>>>>>>>>>>>>>>>>3=4