在很多开发场景中会用到递归,比如菜单权限,网站导航栏,下面我给大家一种简单的递归方法
这是业务层实现方法,根据数据库中parentid去区分当前数据的节点,默认根节点是0,这是必要条件,当然你也可以改,不过好像没啥人会把父级节点设成其他参数吧,处理完的数据直接交给递归调用方法
/**
* 菜单路由
*
* @return
*/
@Override
public List<SpaceSysMenu> level() {
LoginUser loginUser = tokenService.getLoginUser();
// 查询当前登录用户有什么角色
Example e = new Example(SpaceSysUserRole.class);
e.createCriteria().andEqualTo("userId", loginUser.getUserid());
List<SpaceSysUserRole> sysUserRoles = sysUserRoleMapper.selectByExample(e);
List<Long> menuId = null;
for (SpaceSysUserRole userRole : sysUserRoles) {
// 查看当前角色有什么菜单权限
Example e1 = new Example(SpaceSysRoleMenu.class);
e1.createCriteria().andEqualTo("roleId", userRole.getRoleId());
List<SpaceSysRoleMenu> sysRoleMenus = sysRoleMenuMapper.selectByExample(e1);
menuId = sysRoleMenus.stream().map(a -> a.getMenuId()).collect(Collectors.toList());
}
// 查询当前用户拥有什么菜单权限
List<SpaceSysMenu> sysMenuList = sysMenuMapper.selectMenu(menuId);
// 调用递归获取父级下的所有子级节点 默认pid为0是父节点
List menuTree = buildMenuTree(sysMenuList, 0L);
return menuTree;
}
这是调用方法,生成tree,递归的精髓就是自身调用,根据我们的规则生长特定的集合返回给我们
/**
* 递归构建菜单树结构
*
* @param menuList
* @param pid
* @return
*/
private List<SpaceSysMenu> buildMenuTree(List<SpaceSysMenu> menuList, Long pid) {
List<SpaceSysMenu> treeList = new ArrayList<>();
menuList.forEach(menu -> {
if (org.apache.commons.lang.StringUtils.equals(pid.toString(), menu.getParentId().toString())) {
menu.setSubMenus(buildMenuTree(menuList, menu.getId()));
treeList.add(menu);
}
});
return treeList;
}
这是实体,里面会在私有化一个自身类集合,用来存放递归调用过后的数据节点
@Table
@Entity
@ApiModel(value = "菜单管理")
@Data
@EqualsAndHashCode(callSuper = true)
public class SpaceSysMenu extends Base {
private static final long serialVersionUID = 1L;
@ApiModelProperty(value = "父菜单ID,一级菜单为0")
@NotNull(message = "parentId不能为空")
private Long parentId;
@ApiModelProperty(value = "菜单名称")
@NotNull(message = "name不能为空")
private String name;
@ApiModelProperty(value = "菜单URL")
@NotNull(message = "菜单URL不能为空")
private String url;
@ApiModelProperty(value = "类型(1:菜单, 2:按钮)")
@NotNull(message = "type不能为空")
private Integer type;
@ApiModelProperty(value = "权限标识")
private String perms;
@ApiModelProperty(value = "菜单图标")
private String icon;
@ApiModelProperty(value = "菜单级别")
private Integer level;
@ApiModelProperty(value = "排序")
private Integer orderNum;
@ApiModelProperty(value = "顶级目录菜单id")
@NotNull(message = "topMenuId不能为空")
private Long topMenuId;
@ApiModelProperty(value = "状态:0 禁用;1 启用")
@NotNull(message = "status不能为空")
private Integer status;
@ApiModelProperty(value = "创建时间")
private Date createTime;
@ApiModelProperty(value = "修改时间")
private Date updateTime;
private List<SpaceSysMenu> subMenus;
}
第二种写法(迭代器循环递归)
/**
* 递归构建菜单树结构
*
* @param menuList
* @param pid
* @return
*/
private List<SpaceSysMenu> buildMenuTree(List<SpaceSysMenu> menuList, Long pid) {
List<SpaceSysMenu> returnList = new ArrayList<>();
for (Iterator<SpaceSysMenu> iterator = menuList.iterator(); iterator.hasNext(); ) {
SpaceSysMenu t = iterator.next();
// 一、根据传入的某个父节点ID,遍历该父节点的所有子节点
if (t.getParentId() == pid) {
recursionFn(menuList, t);
returnList.add(t);
}
}
return returnList;
}
private void recursionFn(List<SpaceSysMenu> list, SpaceSysMenu t) {
// 得到子节点列表
List<SpaceSysMenu> childList = getChildList(list, t);
t.setSubMenus(childList);
for (SpaceSysMenu tChild : childList) {
if (hasChild(list, tChild)) {
recursionFn(list, tChild);
}
}
}
/**
* 得到子节点列表
*/
private List<SpaceSysMenu> getChildList(List<SpaceSysMenu> list, SpaceSysMenu t) {
List<SpaceSysMenu> tlist = new ArrayList<>();
Iterator<SpaceSysMenu> it = list.iterator();
while (it.hasNext()) {
SpaceSysMenu n = (SpaceSysMenu) it.next();
if (n.getParentId().longValue() == t.getId().longValue()) {
tlist.add(n);
}
}
return tlist;
}
/**
* 判断是否有子节点
*/
private boolean hasChild(List<SpaceSysMenu> list, SpaceSysMenu t) {
return getChildList(list, t).size() > 0 ? true : false;
}
第三种写法(基于jdk1.8函数式编程)
public List<SpaceParkMenu> spaceSite() {
LoginUser loginUser = SessionHelper.getloginUser();
Long companyId = loginUser.getCompanyId();
Long tenantId = loginUser.getTenantId();
Example e = new Example(SpaceParkMenu.class);
e.createCriteria().andEqualTo("tenantId", tenantId).andEqualTo("companyId", companyId).
andEqualTo("status", SpaceParkTypeEnum.Status.ENABLE.getValue());
List<SpaceParkMenu> spaceParkMenus = spaceParkMenuMapper.selectByExample(e);
// ParentId= 0 的根节点
List<SpaceParkMenu> nodeList = spaceParkMenus.stream().
filter(r -> r.getParentId() == 0).
collect(Collectors.toList());
// 为 ParentId=0 设置子节点。递归。
return nodeList.stream().peek(r -> {
// 给每个根结点 设置 SubMenus
List<SpaceParkMenu> dataList = collectTreeData(r, spaceParkMenus);
r.setSubMenus(dataList);
}).collect(Collectors.toList());
}
private List<SpaceParkMenu> collectTreeData(SpaceParkMenu permission, List<SpaceParkMenu> list) {
return list.stream().
filter(r -> r.getParentId().equals(permission.getId())).peek(r -> {
r.setSubMenus(collectTreeData(r, list));
}).collect(Collectors.toList());
}
日常使用没问题