日常项目中经常会有将将带有父子结构的数据构造成树结构数据,比如组织机构数据。之前看到网上很多解决方案,发现构造成的树结构数据字段是封装好的某种特定的类,如果要添加其它返回字段,还需要自己在该类上添加字段。于是我在各位前辈的基础上,通过泛型实现了一套解决方案,并且支持自定义根节点判断。
需要转成树结构的类需要实现该接口,泛型T即为该实体类类型
public interface ITreeNode<T> {
Object id();
Object parentId();
void addChild(T t);
void removeChild(T t);
List<T> children();
}
该函数式接口是用来判断根节点,有一个默认判断,当然也可以自定义实现。默认根节点判断方式为 parentId为null,或者parentId等于给定的值,给定的值可以传进来。
@FunctionalInterface
public interface TreePredicate<T,R> {
boolean test(T t, R r);
static <T extends ITreeNode,R> TreePredicate<T ,R> rootNodePredicate(){
return (T t,R r)->t.parentId() == null || t.parentId().equals(r);
}
}
该类即为构造树结构的类,核心方法TreeBuilder(List<T> list, TreePredicate<T,R> predicate, R r)构造函数,核心思路就是引用传递
public class TreeBuilder<T extends ITreeNode<T>> {
private final LinkedHashMap<Object, T> treeNodesMap = new LinkedHashMap<>();
private final List<T> rootNodesList = new ArrayList<>();
/**
* @param list 平行关系的数据集合
* @param predicate 判断根节点
* @param r 和parentId比较的值,以此判断是否为根节点
* @param <R> 和parentId比较的值的类型
*/
public <R> TreeBuilder(List<T> list, TreePredicate<T,R> predicate, R r){
for(T t : list){
treeNodesMap.put(t.id(), t);
}
treeNodesMap.values().forEach(v->{
if(!predicate.test(v,r)){
T p = treeNodesMap.get(v.parentId());
if(p != null){
p.addChild(v);
}
}else {
rootNodesList.add(v);
}
});
}
public <R> TreeBuilder(List<T> list, R r){
this(list,TreePredicate.rootNodePredicate(),r);
}
protected T getTreeNode(Object id) {
return treeNodesMap.get(id);
}
public List<T> getRoot() {
return rootNodesList;
}
public String treeJsonData(){
return JSONObject.toJSONString(rootNodesList);
}
/**获取某一节点所有子孙节点
* @param id 当前节点id
* @return List<TreeNode>
*/
public List<T> getAllChildren(Object id) {
List<T> allChildren = new ArrayList<>(16);
T treeNode = getTreeNode(id);
for (T t : treeNode.children()) {
allChildren.add(t);
allChildren.addAll(getAllChildren(t.id()));
}
return allChildren;
}
/**获取某一节点所有祖父节点
*/
public List<T> getAllParent(Object id) {
List<T> allParent = new ArrayList<>(16);
T treeNode = getTreeNode(id);
T parent = treeNodesMap.get(treeNode.parentId());
if(parent != null){
allParent.add(parent);
allParent.addAll(getAllParent((parent.id())));
}
return allParent;
}
}
简单测试类,根节点判断为空字符串
@Data
public class OrgDTO implements ITreeNode<OrgDTO> {
@JSONField(ordinal=1)
private String id;
@JSONField(ordinal=2)
private String name;
@JSONField(ordinal=3)
private String parentId;
@JSONField(ordinal=4)
private List<OrgDTO> children = new ArrayList<>();
@Override
public Object id() {
return getId();
}
@Override
public Object parentId() {
return getParentId();
}
@Override
public void addChild(OrgDTO orgDTO) {
children.add(orgDTO);
}
@Override
public List<OrgDTO> children() {
return children;
}
@Override
public void removeChild(OrgDTO orgDTO) {
children.remove(orgDTO);
}
public OrgDTO(String id, String parentId, String name){
this.id = id;
this.parentId = parentId;
this.name = name;
}
public static void main(String[] args) {
List<OrgDTO> list = new ArrayList<>();
list.add(new OrgDTO("1","","1"));
list.add(new OrgDTO("2","1","2"));
list.add(new OrgDTO("3","2","3"));
list.add(new OrgDTO("4","3","4"));
list.add(new OrgDTO("5","4","5"));
TreeBuilder<OrgDTO> tree = new TreeBuilder<>(list,"");
System.out.println(tree.treeJsonData());
}
}
介绍了一个灵活的树结构构建器,适用于将具有父子关系的数据转换为树形结构,支持自定义根节点判断,使用泛型实现,便于扩展和复用。
1万+

被折叠的 条评论
为什么被折叠?



