最近项目中遇到了层级树形结构,数据库存放着的是节点id,以及节点的父节点,刚开始是通过递归节点id作为下一层父节点id的形式,用很多条sql来进行查询直接得出的,,但是由于随着层数以及数据的增加,而且同一层级之间涉及到菜单优先级排序的问题,递归的sql及时是组装一起进行批量查询也需要将近 2s 的时间,这对于一个查询来说无疑是很慢的。
因此后面更换了一种思路,通过一次性查询出所有的节点,最后在进行拼装。
首先设置几个集合
parentMap : 以当前所有节点不为null的parentid作为key,相同的parentid作为value加入到List集合里。
nodeMap: 当前所有节点的id作为key和其对应的所有属性作为value,方便遍历
temp: 因为项目需求需要有一个vo类型的转换,如果没有可以利用原先数据库查出来的
//parent and nodes map
Map<Long, List<MenuVo>> parentMap = new HashMap<>();
//id and node map
Map<Long, MenuVo> nodeMap = new HashMap<>();
//temp list
ArrayList<MenuVo> temp = new ArrayList<>();
//result list
List<MenuVo> vos = new ArrayList<>();
设置好后,接下来开始遍历:
menus.forEach( x -> {
//set parentMap key
Long parentId = x.getParentId();
if(parentId != null && !parentMap.containsKey(parentId)){
parentMap.put(parentId,new ArrayList());
}
nodeMap.put(x.getMenuId(),vo);
});
注意哦,put(parentId,new ArrayList());这里一定要放入一个new List,设置null的话,后面就不能直接添加元素,会报空指针的,
遍历第二次就把所有节点添加到对应的parentMap value下面
遍历第三次的时候,parentMap已经有了父子对应的集合列表,添加到原先的nodeMap childrens下面
遍历第四次的时候,只需要筛选出nodeMap里没有parentId的节点,因为没有父亲节点很明显他们是根节点。
至于第三步的时候是怎么添加到所有节点的childrens下的时候,是因为java的对象是引用传递,由于map的节点数据在赋予他们childrens值的时候,地址指向并未改变,所以当使用 **setChildrens(v)**加入孩子集合的时候,nodeMap 中的对应的节点的childrens内容也会发生改变。
QueryWrapper<Menu> wrapper = new QueryWrapper<>();
wrapper.eq("deleted",0);
wrapper.orderByAsc("menu_seq");
//查询全部
List<Menu> menus = baseMapper.selectList(wrapper);
//parent and nodes map
Map<Long, List<MenuVo>> parentMap = new HashMap<>();
//id and node map
Map<Long, MenuVo> nodeMap = new HashMap<>();
//temp list
ArrayList<MenuVo> temp = new ArrayList<>();
//result list
List<MenuVo> vos = new ArrayList<>();
//遍历一次,提取所有不为空的parent映射到map,映射nodeMap作为所有节点的map,转换vo
menus.forEach( x -> {
MenuVo vo = new MenuVo();
BeanUtils.copyProperties(x,vo);
//set parentMap key
Long parentId = x.getParentId();
if(parentId != null && !parentMap.containsKey(parentId)){
parentMap.put(parentId,new ArrayList());
}
nodeMap.put(x.getMenuId(),vo);
temp.add(vo);
});
//遍历二次,把所有节点添加到对应的parentMap value下面
temp.forEach( x -> {
//add children node
Long parentId = x.getParentId();
if(parentId != null && parentMap.containsKey(parentId)){
List<MenuVo> val = parentMap.get(parentId);
val.add(x);
parentMap.put(parentId,val);
}
});
//遍历三次,此时,parentMap已经有了父子对应的集合列表,添加到原先的nodeMap childrens下面
parentMap.forEach((k,v)->{
//k --> parentId,添加到对应的父亲节点上的childrens中
MenuVo vo = nodeMap.get(k);
vo.setChildrens(v);
});
//遍历四次生成结果返回
nodeMap.forEach((k,v) -> {
if(v.getParentId() == null){
vos.add(v);
}
});
return vos;
以上就是利用完整的的循环取代递归生成树结构的方法