将带有父子结构的数据构造成树结构数据,支持多个根节点

介绍了一个灵活的树结构构建器,适用于将具有父子关系的数据转换为树形结构,支持自定义根节点判断,使用泛型实现,便于扩展和复用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

日常项目中经常会有将将带有父子结构的数据构造成树结构数据,比如组织机构数据。之前看到网上很多解决方案,发现构造成的树结构数据字段是封装好的某种特定的类,如果要添加其它返回字段,还需要自己在该类上添加字段。于是我在各位前辈的基础上,通过泛型实现了一套解决方案,并且支持自定义根节点判断。

需要转成树结构的类需要实现该接口,泛型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());
   }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值