一、起因

之前在技术群里发现了有人问类似于树状关系要怎么查询比较好(具体如下)

java递归实现树形结构 java递归树形结构根据查询_java递归实现树形结构

java递归实现树形结构 java递归树形结构根据查询_java_02

当时在牛客上也发现有人询问了这个问题。当时考虑到了迭代遍历获取子节点加入,然后下面有人提到了递归于是便想着第二天来实现一下、在中午跟家人聚完餐之后就来进行了实现。

二、实现

自己的表:

java递归实现树形结构 java递归树形结构根据查询_List_03

springboot准备:这里使用的mybatis-plus来进行查询(lambdaquery是真的好用)

1、统一返回类

/**
统一返回类
*/
@Data
@ApiModel("result")
public class ResultBean<T> implements Serializable {

    @ApiModelProperty("状态码")
    private int code;
    @ApiModelProperty("返回消息")
    private String message;
    @ApiModelProperty("返回数据")
    private T data;
    /**
     * 返回结果
     * */
    public static<T> ResultBean<T> build(int code , String msg , T data){
        return new ResultBean<>(code,msg , data);
    }

    public static<T> ResultBean<T> ok(T data){
        return new ResultBean<>(data);
    }
//·····一下省略

2、实体类(实际开发需要DTO、vo层的这里我这样写肯定是不规范的)

@Data
@TableName("sql_test_practise")
public class TreeSQL {

    /**
     *逐渐id
     * */
    private Long id;

    /**
     * 父节点id
     * */
    private Long pid;

    /**
     * 内容描述
     * */
    private String content;

    /**
     * 子节点
     * */
    @TableField(exist = false)
    private List<TreeSQL> childNode;
}

3、service层

@Service
public class ITreeSQLService extends ServiceImpl<TreeSQLMapper, TreeSQL> implements TreeSQLService {

}

4、进行测试

1、迭代方式

@GetMapping("/test/1")
public ResultBean<List<TreeSQL>> hello(HttpServletRequest request , HttpServletResponse response){
    List<TreeSQL> treeSQLList = treeSQLService.lambdaQuery().eq(TreeSQL::getPid,"0").list();
    for (TreeSQL treeSQL : treeSQLList){
        List<TreeSQL> childTreeList = treeSQLService.lambdaQuery().eq(TreeSQL::getPid,treeSQL.getId()).list();
        treeSQL.setChildNode(childTreeList);
        for (TreeSQL child : childTreeList) {
            List<TreeSQL> grandChildList = treeSQLService.lambdaQuery().eq(TreeSQL::getPid,child.getId()).list();
            child.setChildNode(grandChildList);
        }
    }
    return  ResultBean.ok(treeSQLList);
}

2、递归方式

@GetMapping("/test/2")
public ResultBean<List<TreeSQL>> test2(){
    //获取数据库中所有的数据
    List<TreeSQL> allList = treeSQLService.list();
    /**
     * 1、在所有的数据中过滤获取一级菜单的数据
     * 2、map中通过递归函数获取一级节点中的子节点列表
     * */
    List<TreeSQL> treeList = allList.stream().filter(parentNode -> parentNode.getPid()==0L)
            .map(node -> getChild(node, allList)).toList();
    return ResultBean.ok(treeList);
}

/**
 * 递归辅助函数
 * @param sql 上级节点
 * @param allList 所有数据
 * */
public TreeSQL getChild(TreeSQL sql , List<TreeSQL> allList){
    /**
     * 1、filter:获取子节点pid是否等于id
     * 2、map:递归获取子节点
     * */
    List<TreeSQL> treeSQL = allList.stream()
            .filter(subNode -> subNode.getPid().equals(sql.getId()))
            .map(subNode -> getChild(subNode, allList)).toList();
    sql.setChildNode(treeSQL);
    return sql;
}

三、比对

1、性能

这里是加了一个过滤器来进行接口性能测试

迭代:

java递归实现树形结构 java递归树形结构根据查询_List_04

递归:

java递归实现树形结构 java递归树形结构根据查询_spring boot_05

可以看到性能提升来快5倍

2、结果

在测试的结果中几乎是完全一样的。但是!!

如果在最后一层节点下在新增一个节点的话例如

java递归实现树形结构 java递归树形结构根据查询_spring boot_06

当前节点有四层,迭代查询的结果由于是两层迭代的原因,所以只能查询到西瓜着一层。

迭代结果:

java递归实现树形结构 java递归树形结构根据查询_spring boot_07

递归结果:

java递归实现树形结构 java递归树形结构根据查询_mybatis_08

这里加入业务变化。原本最大是三级结构、后因为需求变成了四级结构,迭代需要继续加一层for、而递归的代码不需要任何的变化。

代码能够适应业务的变化,可以说耦合度完完全全的降低了。