在 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表的所有字段,此刻就需要对其进行树形结构处理。
树形结构实体类
首先我们需要一个包含自身的树形结构实体类
/**
* 权限树内容
*/
@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属性,即子节点。
代码处理
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后再无数据,便停止递归,进入下一次遍历。