在 Java 中,把List
转换为多级的树形结构非常常见。例如菜单数据是在数据库中“平铺”存储的,在做查询时,需要将其转换为树形结构,方便前端进行展示。
本文就以菜单作为案例,读者可以举一反三,实现自己的需求
数据库设计
将其命名为PERMISSIONS
字段名 |
类型 |
注释 |
CODE |
varchar |
编码(主键) |
PARENT_CODE |
varchar |
父编码 |
NAME |
varchar |
名称 |
TYPE |
tinyint |
类型 0菜单 1按钮 2接口 |
URL |
varchar |
前端路由或后端接口路由 |
CREATE_TIME |
datetime |
创建时间 |
UPDATE_TIME |
datetime |
更新时间 |
Java 处理
假设我们此时已经查询出一个列表List<Permissions>
,Permissions
类是与数据库对应的实体类,包含了PERMISSIONS
表的所有字段,此刻就需要对其进行树形结构处理。
树形结构实体类
首先我们需要一个包含自身的树形结构实体类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
|
@Data @NoArgsConstructor @AllArgsConstructor @EqualsAndHashCode(callSuper = true) @ApiModel(description = "权限树内容") public class PermissionsTreeRes extends Permissions implements Serializable { private static final long serialVersionUID = 3107598961006613967L;
@ApiModelProperty("权限树") private List<PermissionsTreeRes> children; }
|
PermissionsTreeRes
继承了Permissions
,即拥有了数据库表中的所有字段。其自身又包含了类型为List<PermissionsTreeRes>
的children
属性,即子节点。
代码处理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| public List<PermissionsTreeRes> buildPermissionsTree(List<Permissions> permissionsList) { return permissionsList.stream().filter(permissions -> ObjectUtils.isEmpty(permissions.getParentCod())) .map(permissions -> { PermissionsTreeRes treeRes = new PermissionsTreeRes(); BeanUtils.copyProperties(permissions, treeRes); treeRes.setChildren(this.getPermissionsChildrenList(permissions.getCode(),permissionsList)); return treeRes; }).collect(Collectors.toList()); }
private List<PermissionsTreeRes> getPermissionsChildrenList(String code, List<Permissions>permissionsList) { return permissionsList.stream().filter(permissions -> StringUtils.equals(permissions.getParentCode(),code)) .map(permissions -> { PermissionsTreeRes treeRes = new PermissionsTreeRes(); BeanUtils.copyProperties(permissions, treeRes); treeRes.setChildren(this.getPermissionsChildrenList(permissions.getCode(),permissionsList)); return treeRes; }).collect(Collectors.toList()); }
|
buildPermissionsTree()
方法内使用ObjectUtils.isEmpty(permissions.getParentCod())
条件筛选出所有父编码为空的数据,即一级菜单数据。遍历中的setChildren()
调用了getPermissionsChildrenList()
方法,并传入自身的CODE
,去寻找自己的子节点。
getPermissionsChildrenList()
方法内使用递归不断的向下查找,直至filter
后再无数据,便停止递归,进入下一次遍历。