1、定义泛型接口
// 实现接口需要传入两个参数,第一个T 为节点中的id,第二个R 为节点,TreeNode。
public interface TreeNode<T, R extends TreeNode<T, R>> {
/**
* 获取节点id
*
* @return 树节点id
*/
T id();
/**
* 获取该节点的父节点id
*
* @return 父节点id
*/
T parentId();
/**
* 是否是根节点
*
* @return true:根节点
*/
boolean isRoot();
/**
* 设置节点的子节点列表
*
* @param children 子节点
*/
void setChildren(List<R> children);
/**
* 获取所有子节点
*
* @return 子节点列表
*/
List<R> getChildren();
/**
* 是否叶子节点
*
* @return 是否叶子节点
*/
default boolean isLeaf() {
return getChildren().size() == 0;
}
/**
* 列表生成树结构
*
* @param nodes 要生成树状结构的集合
* @param <T> 键类型
* @param <R> 结点类型
* @return 树结构
*/
static <T, R extends TreeNode<T, R>> List<R> generateTree(List<R> nodes) {
List<R> result = new ArrayList<>();
// 从性能考虑。(nodes.size() / .75f) + 1 是HashMap总的空间大小
Map<T, R> id2Node = new LinkedHashMap<>(Math.max((int) (nodes.size() / .75f) + 1, 16));
nodes.forEach(e -> {
id2Node.put(e.id(), e);
e.setChildren(new ArrayList<>());
});
id2Node.forEach((id, node) -> {
if (!node.isRoot()) { // 若非根节点
R parent = id2Node.get(node.parentId()); // 拿到父节点
if (parent != null) {
List<R> children = parent.getChildren(); // 拿到父节点的所有子节点
if (children == null) { // 子节点为null就初始化
children = new ArrayList<>();
parent.setChildren(children);
}
children.add(node); // 将当前节点加到父亲的子节点集合
}
} else { // 根节点
result.add(node);
}
});
return result; // 返回根节点
}
}
2、实现泛型接口
我这里需求是按照树的形式返回所有行政区。比如 省 --> 市 --> 区
因此定义行政区树结构:
@Data
@ApiModel(value = "行政区树结构VO")
// 这里必须实现 TreeNode<T, R> 其中R类型必须继承或实现了 TreeNode
public class DistrictTreeVO implements TreeNode<String, DistrictTreeVO> {
@ApiModelProperty("行政区编码")
private String districtCode;
@ApiModelProperty("行政区名称")
private String districtName;
@ApiModelProperty("父行政区编码")
private String parentDistrictCode;
@ApiModelProperty("子级行政区")
private List<DistrictTreeVO> children;
// 实现泛型接口中未实现的方法。获取id,获取pid,判断是否是根节点
// 泛型接口中已经实现的方法可以直接调用
@Override
public String id() {
return this.getDistrictCode();
}
@Override
public String parentId() {
return this.getParentDistrictCode();
}
@Override
public boolean isRoot() {
return StringUtils.equals(this.getParentDistrictCode(), TradeXzq.MAX_PARENT_DISTRICT_CODE);
}
}
3、查询测试
在调用 TreeNode.generateTree 方法时
DistrictTreeVO 就是 R,String 就是 T。根据 类 DistrictTreeVO 中实现的为准。