目前有个需求场景是这样,有个叶子节点,他对应的父节点是一致的,但是该父节点会在两个不同的根节点下,因此该场景下,该父节点是有两个,但是叶子节点只是一个(因为其code和parentCode是一致的,无法区分成不同的节点),但是生成树状结构时要给个唯一标识符,这会导致两个叶子节点下的唯一标识符一致(因为共用同个对象)。因此解决方案是,在生成树状结构节点的时候,通过深拷贝,将两个节点区分开来,而深拷贝采用json序列化和反序列化实现(此处使用gson序列化和反序列工具)。
具体如下:
public abstract class AbstractTreeNode<T extends AbstractTreeNode> {
/**
* 编码
*/
@ApiModelProperty("编码")
public String code;
/**
* 父类编码
*/
@ApiModelProperty("父类编码")
public String parentCode;
/**
* 文本内容
*/
@ApiModelProperty("文本内容")
public String label;
/**
* 子节点
*/
@ApiModelProperty("子节点")
public List<T> children;
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getParentCode() {
return parentCode;
}
public void setParentCode(String parentCode) {
this.parentCode = parentCode;
}
public String getLabel() {
return label;
}
public void setLabel(String label) {
this.label = label;
}
public List<T> getChildren() {
return children;
}
public void setChildren(List<T> children) {
this.children = children;
}
/**
* 判断是否为根节点(默认为parentCode为null,如果逻辑为parentCode为自身Code,则重写该方法)
* @return
*/
@JsonIgnore
public boolean isRoot(){
return parentCode == null;
}
/**
* 非根节点
* @return
*/
@JsonIgnore
public boolean isNotRoot(){
return !isRoot();
}
}
@ApiModel("归因穿透返回vo")
public class PenetrationReturnVO extends AbstractTreeNode<PenetrationReturnVO> {
/**
* 指标值
*/
@ApiModelProperty("指标值")
private String indicatorValue;
public String getIndicatorValue() {
return indicatorValue;
}
public void setIndicatorValue(String indicatorValue) {
this.indicatorValue = indicatorValue;
}
}
/**
* 转换为树状结构(除根节点外都进行深拷贝),需要继承AbstractTreeNode抽象类(如有必要要重写isRoot方法)
*
* @param abstractTreeNodes
* @return
*/
public static <T extends AbstractTreeNode> List<T> convertToTreeNodeCopy(List<T> abstractTreeNodes) {
if (CollectionUtils.isEmpty(abstractTreeNodes)) {
return Collections.emptyList();
}
Gson gson = GsonUtils.gson();
//过滤掉非根节点的节点,并按父节点分组。(排除根节点的父类是其自身导致的无限循环。)
Map<String, List<T>> maps = abstractTreeNodes.stream().filter(AbstractTreeNode::isNotRoot).collect(Collectors.groupingBy(AbstractTreeNode::getParentCode));
List<T> roots = abstractTreeNodes.stream().filter(AbstractTreeNode::isRoot).collect(Collectors.toList());
for (T root : roots) {
List<T> abstractTreeNodesRes = queryChildren(root.getCode(), maps);
//深拷贝对象
List<T> res =
abstractTreeNodesRes.stream().map(a -> (T)gson.fromJson(gson.toJson(a), a.getClass())).collect(Collectors.toList());
root.setChildren(res);
}
return roots;
}
/**
* 递归实现查询子节点
*
* @param parentCode
* @param maps
* @return
*/
private static <T extends AbstractTreeNode> List<T> queryChildren(String parentCode, Map<String, List<T>> maps) {
List<T> nodes = maps.get(parentCode);
if (CollectionUtils.isEmpty(nodes)) {
return Collections.emptyList();
}
for (T node : nodes) {
//排除父节点是其自身的死循环,只走一次。
if (!parentCode.equals(node.getCode())) {
node.setChildren(queryChildren(node.getCode(), maps));
} else {
node.setChildren(Collections.emptyList());
}
}
return nodes;
}
public class GsonUtils {
private static Gson gson;
static {
gson = new Gson();
}
public static Gson gson(){
return gson;
}
}
上面的主要知识点就是如何在序列化和反序列化中获取实际的泛型类型,此处是通过子类集成父类并说明泛型类型实现的,具体知识点可以看我这篇博客。
获取泛型对象实际类型