提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
一、列表参数
列表参数需要符合递归所需的关联关系,例如必须父子级关系(子数据的parentId需要为父级的id),未进行关联的数据无法在树形结构中显示。
package com.vshi.util.entity;
import lombok.Data;
@Data
public class TestTree {
private String id;
private String name;
private String parentId;
private Integer sort;
private String remake;
}
二、响应数据适配(通用接口)
1. 接口方法构建
利用重写方法存储数据,通用适配接口
package com.vshi.util.tree.entity;
import java.util.List;
public interface TreeNode {
String getId();
String getName();
Integer getSort();
String getParentId();
void setChildNodes(List<? extends TreeNode> childNodes);
List<? extends TreeNode> getChildNodes();
}
2. 创建响应实体类
利用类的继承和接口实现来构建实体类,增加代码的可读性和复用性。
package com.vshi.util.entity;
import java.util.List;
public class TestTreeVo extends TestTree implements TreeNode {
private List<TestTreeVo> childNodes;
@Override
public void setChildNodes(List<? extends TreeNode> childNodes) {
this.childNodes = (List<TestTreeVo>) childNodes;
}
@Override
public List<? extends TreeNode> getChildNodes() {
return this.childNodes;
}
}
三、树形结构构建
package com.vshi.util;
import com.vshi.util.entity.TreeNode;
import org.springframework.beans.BeanUtils;
import org.springframework.util.CollectionUtils;
import java.util.*;
import java.util.stream.Collectors;
public class TreeNodeUtil {
public static final String ROOT_ID = "0";
public static <T> List<TreeNode> buildTreeNode(List<? extends TreeNode> list, Class<T> clazz) {
if (CollectionUtils.isEmpty(list)) {
return Collections.emptyList();
}
List<TreeNode> dtoList = new ArrayList<>();
list.forEach(x -> {
T entity = BeanUtils.instantiateClass(clazz);
TreeNode treeDto = (TreeNode) adapt(x, entity);
dtoList.add(treeDto);
});
return buildTree(dtoList, clazz);
}
private static <T> List<TreeNode> buildTree(List<TreeNode> dtoList, Class<T> clazz) {
Map<String, List<TreeNode>> map = new HashMap<>(dtoList.size());
dtoList.forEach(x -> {
T entity = BeanUtils.instantiateClass(clazz);
TreeNode nodeVO = (TreeNode) adapt(x, entity);
BeanUtils.copyProperties(x, nodeVO);
List<TreeNode> childNodes = map.getOrDefault(x.getParentId(), new ArrayList<>());
childNodes.add(nodeVO);
List<TreeNode> collect = childNodes.stream().sorted(Comparator.comparing(TreeNode::getSort)).collect(Collectors.toList());
map.put(x.getParentId(), collect);
});
List<TreeNode> roots = map.get(ROOT_ID);
processNode(roots, map);
return roots;
}
private static void processNode(List<TreeNode> roots, Map<String, List<TreeNode>> map) {
if (CollectionUtils.isEmpty(roots)) {
return;
}
roots.forEach(x -> {
List<TreeNode> child = map.get(x.getId());
if (!CollectionUtils.isEmpty(child)) {
processNode(child, map);
x.setChildNodes(child);
}
});
}
private static <T> Object adapt(TreeNode x, T entity) {
BeanUtils.copyProperties(x, entity);
return entity;
}
}
三、总结
两个需要注意的地方,一是构建一个通用的处理接口,可以在通用方法中进行数据的转换;二是利用map集合存储同节点下的数据集,最后递归构建Tree数据。
|