当前位置: 首页 > 知识库问答 >
问题:

java - 列表转树结构,以及树结构转列表应该怎么做?有没有通用的方法?

袁雅逸
2024-10-07

单纯列表转树的逻辑我是清楚的,不管用什么实现方案,核心应该都是根据 id 找到父节点,并将当前节点添加到父节点的子节点列表属性中。

我的问题是在java 中能不能写一个通用的实现?

比如:只有一个资源实体类需要转树时,那直接写一个与资源实体类相对应的节点类,这个节点类不仅有资源实体类的所有属性,而且还多一个子节点列表属性就可以了。但如果有多个需要转换树结构的资源呢?总不能给每一个需要转换的资源实体类都创建对应的节点类吧?而且即便是只有一个,也是有弊端的,如果实体类有几百个属性,那这几百个属性还要挨个重写一遍?

我现在想到了两种方案,但各有弊端,具体如下:
第一:利用泛型,建一个通用的 TreeNode,就像这样:

public class TreeNode<T>  {
    private T PO;
    private List<TreeNode> childList;
    ....
}

这样就是将实体类对象用泛型保存,子节点列表在外面单独用一个属性保存,不管有多少各类需要转换,也不管需要被转换的类型里有多少属性都可以用,但这样的话,子节点列表就和其他属性割裂开了,对前端或其他树状结构数据消费者可能是不友好。
第二:利用继承,将包含 childList 的 TreeNode 作为所有需要实现列表转树的父类。但这样的弊端就更明显了,比如有一个需要转换树节点的类已经继承了其他类怎么办?而且很多甚至大部分需要转换的都是实体类,而实体上是没有childList 的,这样做虽然没有直接改实体类,但是在父类上加了childList,其对象也是会有childList 属性的,那这还能算是一个实体对象吗是不是有点不合适?

目前就想到以上两种方案,但是都不尽人意,项目上大概率不会采用。希望有大佬提供更成熟的方案指点一二,不甚感激。

共有3个答案

梁季
2024-10-07

喜欢重复造轮子?这不是有现成的?干嘛不用呢?
https://doc.hutool.cn/pages/TreeUtil/

鲁辉
2024-10-07

写一个泛型函数,类型就是T ,然后参数有获取父id.获取id,设置children这些

   public TreeUtils(Function<T, String> parentIdFunction, Function<T, String> idFunction,
                     BiConsumer<T, List<T>> setChildrenFunction, Function<T, List<T>> getChildrenFunction) {
        this.parentIdFunction = parentIdFunction;
        this.idFunction = idFunction;
        this.setChildrenFunction = setChildrenFunction;
        this.getChildrenFunction = getChildrenFunction;
    }

大体这个意思,然后你再写个函数,扔个list,去处理就行,反正处理tree的本质就是塞children

高鸿振
2024-10-07

你都想到继承了,那为啥不多考虑一下,试试接口?
再一个咱们反射不能用啊?

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.Data;
import org.apache.commons.lang3.StringUtils;

import java.util.ArrayList;
import java.util.List;

public class TreeTest {

public static void main(String[] args) throws Exception {

    List<User> users = new ArrayList<>();
    users.add(new User("1","湖南省",null));
    users.add(new User("2","广东省",null));
    users.add(new User("3","长沙市","1"));
    users.add(new User("4","深圳市","2"));
    users.add(new User("5","岳麓区","3"));
    users.add(new User("6","望城坡街道","5"));
    users.add(new User("7","湘潭市","1"));
    List<? extends TreeNode> tree = tree(users);

    ObjectMapper objectMapper = new ObjectMapper();
    System.out.println(objectMapper.writeValueAsString(tree));
}


public static <T extends TreeNode> List<T> tree(List<T> list){
    List<T> headerNodes = new ArrayList<>();
    // 构造顶级节点
    for (TreeNode tree : list) {
        if(StringUtils.isEmpty(tree.getPid())){
            headerNodes.add((T) tree.newInstance());
        }
    }
    // 循环将当前节点加入到父节点的集合中
    for (T t : list) {
        parentNodeAddChild(t,headerNodes);
    }
    return headerNodes;
}


private static <T extends TreeNode>  void parentNodeAddChild(TreeNode<T> node,List<T> parentNodes){
    for (TreeNode treeNode : parentNodes) {
        // 找到父节点,直接加入子集并且结束循环
        if(StringUtils.equals(node.getPid(),treeNode.getId())){
            treeNode.getChildList().add(node);
            break;
        }
        // 递归循环父节点
        parentNodeAddChild(node, treeNode.getChildList());
    }
}

}

interface TreeNode<T>{

String getPid();

String getId();

List<T> getChildList();

String getText();

<T extends TreeNode> T newInstance();

}

@Data
@JsonIgnoreProperties({"name","children"})
class User implements TreeNode<User>{

public User(String id, String name, String pid) {
    this.id = id;
    this.name = name;
    this.pid = pid;
    this.children = new ArrayList<>();
}

@Override
public String getPid() {
    return pid;
}

@Override
public List<User> getChildList() {
    return children;
}

@Override
public String getText() {
    return name;
}

@Override
public User newInstance() {
    return new User(id,name,pid);
}

@Override
public String toString() {
    return "User{" +
            "id='" + id + '\'' +
            ", getText()='" + name + '\'' +
            ", pid='" + pid + '\'' +
            ", children=" + children +
            '}';
}

private String id;

private String name;

private String pid;

private List<User> children;

}

 类似资料:
  • 如下图: 根据某一个相同的元素合并单元格,然后又有树形的表格该怎么做? 各位大佬能否给个demo

  • 问题内容: 我在列表中有一个字符串路径集合,例如[“ x1 / x2 / x3”,“ x1 / x2 / x4”,“ x1 / x5”]。我需要从该列表构造一个树状结构,可以对其进行迭代以获得漂亮的打印树。像这样 有什么想法/建议吗?我认为可以通过处理字符串列表来首先解决该问题。编辑:选择的正确答案是一种优雅的实现,其他建议也很好。 问题答案: 遵循可访问树的幼稚实现的实现: 访客模式的界面: 访

  • 一、想要实现的效果 二、我的代码 public class People { } public static void main(String[] args) { 三、代码执行结果 四、解释 我递归用的少,用起来有点不达意。上面自己写的递归方法虽然能实现递归效果,但问题很大。 4.1、我的方法返回的只是命中项,而不是一个树的结构(除非第一层就命中了)。 4.2、返回的树里没有剔除未命中项。 上述两

  • 蓝牙设备对象​ 证书对象​ 证书主体对象​ Cookie对象​ 崩溃报告对象​ 桌面捕获源对象​ 显示器对象 打印机信息对象 文件过滤器对象​ 最近使用的项目​ 常用列表项​ CPU使用率对象即程序占用的CPU资源​ IO值对象​ 内存信息对象​ 进程内存信息对象​ 内存使用详细信息​ Mime类型缓冲区​ 矩形对象​ 删除客户端证书对象​ 删除密码对象​ Scrubber项对象​ 分段控制对象​

  • 我现在正在做一个Java-Spring启动项目。我有一个API_endpoint,有4个完全相同的字段。所以字段1(用户名)、字段2(名字)、字段3(姓氏)和字段4。字段1-3很简单,我可以通过等方式分配它们。 但Field4则不同。这是一个嵌套的结构,它有时会有所不同。 例如,在一个请求中,它类似于: 下一次就像 这可能多达200个字段。 换句话说:Field4是一个复杂的嵌套结构,可以在每个请

  • 我有一个带有< code>parent字段的“page”对象列表。该父字段引用了列表中的另一个对象。我想从基于此字段的列表中创建一个树层次结构。 以下是我的原始列表: 我想把它转换成这样的树形结构: 我希望有一个可重用的函数,我可以随时对任意列表调用它。有谁知道处理这个问题的好方法吗?任何帮助或建议将不胜感激!