这里介绍一个将实体类数组通过父级id字段转换成树型结构的方法,一般主要还是便于接口数据返回后,前端可以直接使用和展示。
这里首先建一个基类,用于实现后面写的转换方法,然后让任意实体类继承这个类,再调用转换方法即可
import java.util.ArrayList;
import java.util.List;
/**
* @author Wus
* @since 2022/8/17-16:16
*/
public class TreeNode {
private Object parentId;
private List<TreeNode> children;
private Object id;
private Long weight;
public void add(TreeNode node) {
if (children == null) {
children = new ArrayList<>();
}
children.add(node);
}
public Object getParentId() {
return parentId;
}
public void setParentId(Object parentId) {
this.parentId = parentId;
}
public List<TreeNode> getChildren() {
return children;
}
public void setChildren(List<TreeNode> children) {
this.children = children;
}
public Object getId() {
return id;
}
public void setId(Object id) {
this.id = id;
}
public Long getWeight() {
return weight;
}
public void setWeight(Long weight) {
this.weight = weight;
}
}
下面这个就是转换方法,只要把集合传进去即可,默认根节点是root(String),null或者0(Integer),别忘了将id,parentId属性是必须补充上的,weight主要用于排序,可以为null。
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @author Wus
* @since 2022/8/17-16:12
*/
@Slf4j
public class TreeConvert {
/**
* 默认0,null,root是根节点
*
* @param origList 原始数据
* @return 树结构数据
*/
public static <T extends TreeNode> List<T> convert(List<T> origList) {
// 用于保存当前 id 索引的实体类
Map<Object, T> idEntities = new HashMap<>(5);
// 暂存区, 用于保存没有找到父 id 的控件
List<T> tempList = new ArrayList<>();
// 最终生成结果
List<T> result = new ArrayList<>();
log.info("开始转换树形结构");
log.info("原始数据为:{}", origList);
for (T entity : origList) {
// 获取 id, parentId, children
Object id = entity.getId();
Object parentId = entity.getParentId();
idEntities.put(id, entity);
if (ObjectUtils.isEmpty(parentId) || "root".equals(parentId) || parentId.equals(Integer.parseInt("0"))) {
// 如果父 id 为空, 则实体类为第一层
sortInsert(result, entity);
} else {
// 根据父 id 获取实体类
T parentEntity = idEntities.get(parentId);
if (parentEntity == null) {
// 没找到先放入暂存区
tempList.add(entity);
} else {
// 父组件判断是否存在 children, 不存在新增, 存在则直接假如
setChildrenValue(entity, parentEntity);
}
}
}
// 处理暂存区, 暂存区的一定不为根节点, 所以它只要父节点存在, 那么此轮查询一定能找到父节点(上一轮已经将全部节点放入idEntities)
for (T entity : tempList) {
// 根据父id获取实体类
T parentEntity = idEntities.get(entity.getParentId());
// 父组件判断是否存在children, 并将entity加入children
setChildrenValue(entity, parentEntity);
}
return result;
}
/**
* 设置children
* @param entity 子节点
* @param parentEntity 父节点
*/
private static <T extends TreeNode> void setChildrenValue(T entity, T parentEntity) {
List<TreeNode> children = parentEntity.getChildren();
List<T> childrenList;
if (children == null) {
childrenList = new ArrayList<>();
sortInsert(childrenList, entity);
parentEntity.setChildren((List<TreeNode>) childrenList);
} else {
sortInsert(children, entity);
}
}
/**
* 排序插入,依据weight属性
*/
public static <T extends TreeNode> void sortInsert(List<T> list, T entity) {
if (entity.getWeight() == null) {
list.add(entity);
} else {
//weight转换为大数
BigDecimal currentValue = new BigDecimal(entity.getWeight());
if (list != null && list.size() != 0) {
for (int i = 0; i < list.size(); i++) {
BigDecimal sortValue = new BigDecimal(list.get(i).getWeight());
if (currentValue.compareTo(sortValue) < 0) {
list.add(i, entity);
return;
}
}
}
assert list != null;
list.add(entity);
}
}
}
使用示例:
下面就做一个使用示例
首先继承TreeNode类,id,parentId,weight可以重新声明也可以super来用父类的属性。
import com.fasterxml.jackson.annotation.JsonFormat;
import com.wss.server.question.entity.QClassification;
import com.wss.witch.utils.tree.TreeConvert;
import com.wss.witch.utils.tree.TreeNode;
import io.swagger.annotations.ApiModelProperty;
import java.io.Serial;
import java.io.Serializable;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
/**
* @author Wus
* @since 2022/8/17-16:19
*/
public class ClassQuestionTreeVo extends TreeNode implements Serializable {
@Serial
private static final long serialVersionUID = 22494L;
@ApiModelProperty("分类id")
private Integer qClassificationId;
@ApiModelProperty("分类名")
private String className;
@ApiModelProperty("父级id")
private Integer parentId;
@ApiModelProperty("层级")
private Integer level;
@ApiModelProperty("创建时间")
@JsonFormat(locale = "zh", timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime createTime;
@ApiModelProperty("修改时间")
@JsonFormat(locale = "zh", timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime updateTime;
@ApiModelProperty("创建用户")
private Long createUser;
@ApiModelProperty("是否删除")
private Integer isDelete;
@ApiModelProperty("图标")
private String logo;
@ApiModelProperty("权重")
private Long weight;
/**
* 初始化
*/
public static ClassQuestionTreeVo init(QClassification qClassification) {
ClassQuestionTreeVo treeVo = new ClassQuestionTreeVo();
treeVo.setId(qClassification.getqClassificationId());
treeVo.setParentId(qClassification.getParentId());
treeVo.setqClassificationId(qClassification.getqClassificationId());
treeVo.setClassName(qClassification.getClassName());
treeVo.setLevel(qClassification.getLevel());
treeVo.setCreateTime(qClassification.getCreateTime());
treeVo.setUpdateTime(qClassification.getUpdateTime());
treeVo.setCreateUser(qClassification.getCreateUser());
treeVo.setIsDelete(qClassification.getIsDelete());
treeVo.setLogo(qClassification.getLogo());
return treeVo;
}
/**
* 转换成树形结构
*/
public static List<ClassQuestionTreeVo> convert(List<QClassification> qClassifications) throws Exception {
ArrayList<ClassQuestionTreeVo> classQuestionTreeVos = new ArrayList<>();
for (QClassification qClassification : qClassifications) {
classQuestionTreeVos.add(init(qClassification));
}
return TreeConvert.convert(classQuestionTreeVos);
}
public Integer getqClassificationId() {
return qClassificationId;
}
public void setqClassificationId(Integer qClassificationId) {
this.qClassificationId = qClassificationId;
}
public String getClassName() {
return className;
}
public void setClassName(String className) {
this.className = className;
}
@Override
public Integer getParentId() {
return parentId;
}
public void setParentId(Integer parentId) {
this.parentId = parentId;
}
public Integer getLevel() {
return level;
}
public void setLevel(Integer level) {
this.level = level;
}
public LocalDateTime getCreateTime() {
return createTime;
}
public void setCreateTime(LocalDateTime createTime) {
this.createTime = createTime;
}
public LocalDateTime getUpdateTime() {
return updateTime;
}
public void setUpdateTime(LocalDateTime updateTime) {
this.updateTime = updateTime;
}
public Long getCreateUser() {
return createUser;
}
public void setCreateUser(Long createUser) {
this.createUser = createUser;
}
public Integer getIsDelete() {
return isDelete;
}
public void setIsDelete(Integer isDelete) {
this.isDelete = isDelete;
}
public String getLogo() {
return logo;
}
public void setLogo(String logo) {
this.logo = logo;
}
@Override
public Long getWeight() {
return weight;
}
@Override
public void setWeight(Long weight) {
this.weight = weight;
}
@Override
public String toString() {
return "ClassQuestionTreeVo{" +
"qClassificationId=" + qClassificationId +
", className='" + className + '\'' +
", parentId=" + parentId +
", level=" + level +
", createTime=" + createTime +
", updateTime=" + updateTime +
", createUser=" + createUser +
", isDelete=" + isDelete +
", logo='" + logo +
",id=" + super.getId() +
",weight=" + weight +
'}';
}
}
这一块有些其他类的细节就不放了,上主要是把数据库的实体类变成方便转换的类,然后通过TreeConvert.convert
转换即可。
public static List<ClassQuestionTreeVo> convert(List<QClassification> qClassifications) throws Exception {
ArrayList<ClassQuestionTreeVo> classQuestionTreeVos = new ArrayList<>();
for (QClassification qClassification : qClassifications) {
classQuestionTreeVos.add(init(qClassification));
}
return TreeConvert.convert(classQuestionTreeVos); //----------这是重点
}