递归树形结构的多级分类数据封装

         在日常开发中,我们经常需要查询一些树形结构多级分类数据,如:多级菜单、商品三级分类、企业组织架构等等。

        我们以商品三级分类为例。大部分情况下,在同一张数据表中,无论是一级商品还是三级商品,每一条商品信息独占一行空间,通过“层级”字段,标明该商品属于第几级,通过“父id”字段,判断该商品是否被其他商品包括其中,如:

但是,客户端请求分级数据,如果我们直接将表中的数据通过“select * from t_table”的方式响应给前端,前端将很难将其转化成三级分类的效果并展示出来,通常,我们会将查表的数据做层级处理,再将这种分层的数据响应给前端,如:

       

         所以,我们对于这种查询分级信息通常的处理方式是:先在实体类中添加一个属性List<T>,用来封装该商品的子类商品集合:

public class CategoryEntity implements Serializable {
    private static final long serialVersionUID = 1L;
    /**
     * 分类id
     */
    @TableId
    private Long catId;
    /**
     * 分类名称
     */
    private String name;
    /**
     * 父分类id
     */
    private Long parentCid;
    /**
     * 层级
     */
    private Integer catLevel;
    /**
     * 子菜单
     */
	@TableField(exist = false)
    @JsonInclude(JsonInclude.Include.NON_EMPTY)
    private List<CategoryEntity> children;

}

首先,查表获得所有商品信息,再遍历集合,过滤出所有一级商品,然后,通过递归的方式, 将子类商品按照分类等级封装到一级商品实体类的List<T>集合中。

        事实上,所有的多级分类信息我们都是通过递归查找然后将组装好的树形结构信息响应给前端。问题在于,再一个项目中,用到这种分级查询的情况还挺多的,比如:多级菜单、商品三级分类、企业组织架构等等。如果每个用到该功能的地方都写一遍代码,确实会造成一些代码的冗余。那么,我们能否尝试将递归封装的过程抽取出来,创建一个静态方法,当我程序中需要用到分级信息封装的时候,只需要调用方法,将查表得到的list传递过去,获得封装好的分级对象呢?这里,我做了一个尝试:

        第一步:创建一个“基础树形结构”类BaseTreeVo<T>

/**
 * 基础树形结构 vo类
 *
 * @param <T> 泛型
 * @author tg
 */
@Data
public class BaseTreeVo<T> {
    /**
     * 主键id
     */
    private Long catId;
    /**
     * 父id
     */
    private Long parentCid;
    /**
     * 子类集合
     */
    private List<T> children = new ArrayList<T>();
}

        第二步:创建一个要响应给前端的vo类,该vo类继承BaseTreeVo<T>

/**
 * 商品分类信息 vo类
 *
 * @author gaozz
 */
@Data
public class CategoryVo extends BaseTreeVo<CategoryVo> {
    ...
}

        第三步:将查表得到的list<entity>转换成list<vo>

// 1、查出表中所有商品分类信息
List<CategoryEntity> categories = baseMapper.selectList(null);
// 封装商品分类信息
List<CategoryVo> collect = categories.stream().map(CategoryVo::new).collect(Collectors.toList());

        第四步:调用树形结构工具类的方法,将第三步得到的list<vo>传递过去,进行递归封装。在工具类中,事先封装好了一个静态方法:

/**
 * 树形结构工具
 *
 * @author tg
 * @date 2022-11-01 23:16:24
 */
public class BaseTreeUtils {

    /**
     * 获取树形结构
     *
     * @param allList 树形列表
     * @param <T>     泛型
     * @return 树形结构
     */
    public static <T extends BaseTreeVo> List<T> listTreeNodes(List<T> allList) {
        // 创建一级分类集合
        List<T> parentList = new ArrayList<>();
        if (!CollectionUtils.isEmpty(allList)) {
            // 过滤出所有的一级分类
            parentList = allList.stream().filter(item -> item.getParentCid() == 0).collect(Collectors.toList());
        }
        //返回的树形节点数据
        if (!CollectionUtils.isEmpty(parentList)) {
            for (T parentNode : parentList) {
                //递归查询所有子节点
                parentNode.setChildren(recursiveTree(parentNode, allList));
            }
        }
        return parentList;
    }

    /**
     * 递归算法解析成树形结构
     *
     * @param parentNode   父级对象
     * @param classifyList 集合
     * @param <T>          泛型
     * @return 树形结构
     */
    public static <T extends BaseTreeVo> List<T> recursiveTree(T parentNode, List<T> classifyList) {
        List<T> childList = new ArrayList<>();
        //子集的直接子对象
        for (T entity : classifyList) {
            Long parentId = entity.getParentCid();
            if (parentNode.getCatId() == parentId) {
                childList.add(entity);
            }
        }
        //子集的间接子对象
        for (T entity : childList) {
            entity.setChildren(recursiveTree(entity, classifyList));
        }
        //递归退出条件
        if (childList.size() == 0) {
            return null;
        }
        return childList;
    }
}

该方法的逻辑是:先遍历集合,获取所有的一级分类,然后遍历获得到的一级分类集合,再一级分类信息中再递归查找是否有子分类属于该一级分类,如果有,按照层级放到对应分类的子类集合children中。

        最后,运行程序,得到了我们预期想要的结果:

        如此,我们便实现了递归树形结构的多级分类数据封装,当我们需要用到分类信息显示时,可以调用BaseTreeUtils.listTreeNodes(collect)方法,实现功能,也提高了程序的复用性。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值