递归调用的实际应用

时间:2022-10-27 14:09:27

一、业务场景

  项目开发中,一般是不推荐使用递归调用的,因为递归调用很占用内存,并且一个不留神就可能变成死递归,

整个项目可能都会因为这个递归调用而挂掉,造成非常严重的后果。典型案例就是可以在电脑上面递归调用创建

文件夹,会直接损坏电脑上的硬盘。以前亲自见到过好奇心重的人干这事,结果直接让某个磁盘废掉。所以递归的

代码一般都会慎用,能不用就不用。

二、需求分析

  今天写的这篇日志主要是针对特殊场景下使用递归来解决问题。比如机构数据,以贵州省的数据为例,下面有

贵阳市,铜仁市;贵阳市下面有南明区,云岩区。铜仁市下面有碧江区,万山区。碧江区下面又有北门社区,环北社区。

机构是分层级的,存在子父级关系,比如父一级的id是子一级的父id。整个数据结构的形式就是一个树形结构。数据

处理的时候,前端需要的是树形结构的数据,便于进行处理。由于数据不多,因此就一次性返给前端。问题是如何

构建这种子父级的关系呢?能够确定的一点就是能够拿到最高一级机构的id,就可以获取到最高机构的数据,然后获取

子机构数据的时候,就需要程序来进行处理。通过循环也能够解决问题,比如通过一级机构贵州省,可以获取到二级机构

贵阳市和铜仁市的数据,然后在分别去获取他们子机构的数据,多轮循环也能解决问题,只是这种处理方式比较麻烦,不优雅。

自己能想到的办法就是采取递归调用的方式进行处理。这是最简单,最便捷的方式,但是一定要确保自己写的代码的正确性。

否则还是使用多轮循环处理好些。

三、解决方案

  首先看一个简单的递归调用的例子,网上的各种博文讲得最多的就是求一个数的阶乘,使用递归来做非常方便。

示例代码如下:

public class RecursionTest {

    public static void main(String[] args) {

        int result = factorial(3);

        System.out.println("阶乘计算结果--->" + result);

    }

 

    /* @Description: 递归方法

     * @author: yilang

     * @date: 2022/10/27 11:15

     * @param: a

     * @return: int

     */

    public static int factorial(int a) {

        if (a == 1) {

            return a;

        } else {

            return a * factorial(a - 1);

        }

    }

}

测试结果如下.

 递归调用的实际应用

设计一张简单的机构表:

CREATE TABLE `gz_organization_code` (

  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'id',

  `region_code` varchar(32) NOT NULL COMMENT '区域编码',

  `parent_region_code` varchar(20) DEFAULT NULL COMMENT '父级编码',

  `region_level` tinyint(4) DEFAULT NULL COMMENT '级别',

  `region_name` varchar(20) DEFAULT NULL COMMENT '区域名称',

  PRIMARY KEY (`id`) USING BTREE

) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8 COMMENT='机构编码表';

插入10条数据,SQL如下.

INSERT INTO `applets`.`gz_organization_code` (`id`, `region_code`, `parent_region_code`, `region_level`, `region_name`) VALUES ('1', '0001', '0', '1', '贵州省');

INSERT INTO `applets`.`gz_organization_code` (`id`, `region_code`, `parent_region_code`, `region_level`, `region_name`) VALUES ('2', '0002', '0001', '2', '贵阳市');

INSERT INTO `applets`.`gz_organization_code` (`id`, `region_code`, `parent_region_code`, `region_level`, `region_name`) VALUES ('3', '0003', '0001', '2', '铜仁市');

INSERT INTO `applets`.`gz_organization_code` (`id`, `region_code`, `parent_region_code`, `region_level`, `region_name`) VALUES ('4', '00021', '0002', '3', '南明区');

INSERT INTO `applets`.`gz_organization_code` (`id`, `region_code`, `parent_region_code`, `region_level`, `region_name`) VALUES ('5', '00022', '0002', '3', '云岩区');

INSERT INTO `applets`.`gz_organization_code` (`id`, `region_code`, `parent_region_code`, `region_level`, `region_name`) VALUES ('6', '00031', '0003', '3', '碧江区');

INSERT INTO `applets`.`gz_organization_code` (`id`, `region_code`, `parent_region_code`, `region_level`, `region_name`) VALUES ('7', '00032', '0003', '3', '万山区');

INSERT INTO `applets`.`gz_organization_code` (`id`, `region_code`, `parent_region_code`, `region_level`, `region_name`) VALUES ('8', '0003101', '00031', '4', '北门社区');

INSERT INTO `applets`.`gz_organization_code` (`id`, `region_code`, `parent_region_code`, `region_level`, `region_name`) VALUES ('9', '0003102', '00031', '4', '环被社区');

INSERT INTO `applets`.`gz_organization_code` (`id`, `region_code`, `parent_region_code`, `region_level`, `region_name`) VALUES ('10', '0003103', '00031', '4', '市中街道');

之后根据这个简单的递归调用方法,来写当前机构子机构处理的代码。经过反复的修改与调试,最终完成代码,

测试调试通过。示例代码如下,

 // 一次性查询出全部的数据

@GetMapping("/region")
public List<RegionCodePo> regioncode(){
List<RegionCodePo> regionCodes = this.testMapper.queryGZRegionCodeList();
regionCodes = handlerRegionCodes(regionCodes);
return regionCodes;
}

    private List<RegionCodePo> handlerRegionCodes (List<RegionCodePo> regionCodes) {

        if (regionCodes == null || regionCodes.size() < 1) {

            return Collections.emptyList();

        }

        RegionCodePo GZSRegionCode = null;

        for (RegionCodePo regionCodePo : regionCodes) {

            if ("0".equals(regionCodePo.getParentRegionCode())) {

                // 获取第一级贵州省的区域编码

                GZSRegionCode = regionCodePo;

                break;

            }

        }

        handlerChildRegionCodes(regionCodes, GZSRegionCode);

        regionCodes.clear();

        regionCodes.add(GZSRegionCode);

        return regionCodes;

    }

 

    // 处理子机构数据

    private RegionCodePo handlerChildRegionCodes (List<RegionCodePo> regionCodes, RegionCodePo parentRegionCodePo) {

        if (regionCodes == null || regionCodes.size() < 1) {

            return parentRegionCodePo;

        }

        List<RegionCodePo> childRegionCodes = new ArrayList<>();

        for (RegionCodePo regionCodePo : regionCodes) {

            if (parentRegionCodePo.getRegionCode().equals(regionCodePo.getParentRegionCode())) {

                // 有子机构,则递归调用

                handlerChildRegionCodes(regionCodes, regionCodePo);

                // 添加单条子机构数据

                childRegionCodes.add(regionCodePo);

            }

        }

        if (childRegionCodes == null || childRegionCodes.size() < 1) {

            return parentRegionCodePo;

        }

        // 添加子机构数据

        parentRegionCodePo.setChildRegionCodes(childRegionCodes);

        return parentRegionCodePo;

}

 

/**

 * @Author 一只爱阅读的程序员

 * @Description 区域代码

 * @Date 2022/10/27 0:06

 * @Version 1.0

 */

@Data

public class RegionCodePo {

    /*

     * id

     */

    private Integer id;

 

    /*

     * 区域编码

     */

    private String regionCode;

 

    /*

     * 区域父编码

     */

    private String parentRegionCode;

 

    /*

     * 区域级别

     */

    private String regionLevel;

 

    /*

     * 区域名称

     */

    private String regionName;

 

    /*

     * 子机构

     */

    private List<RegionCodePo> childRegionCodes;

}

返回结果如下.

[{"id":1,"regionCode":"0001","parentRegionCode":"0","regionLevel":"1","regionName":"贵州省","childRegionCodes":[{"id":2,"regionCode":"0002","parentRegionCode":"0001","regionLevel":"2","regionName":"贵阳市","childRegionCodes":[{"id":4,"regionCode":"00021","parentRegionCode":"0002","regionLevel":"3","regionName":"南明区","childRegionCodes":null},{"id":5,"regionCode":"00022","parentRegionCode":"0002","regionLevel":"3","regionName":"云岩区","childRegionCodes":null}]},{"id":3,"regionCode":"0003","parentRegionCode":"0001","regionLevel":"2","regionName":"铜仁市","childRegionCodes":[{"id":6,"regionCode":"00031","parentRegionCode":"0003","regionLevel":"3","regionName":"碧江区","childRegionCodes":[{"id":8,"regionCode":"0003101","parentRegionCode":"00031","regionLevel":"4","regionName":"北门社区","childRegionCodes":null},{"id":9,"regionCode":"0003102","parentRegionCode":"00031","regionLevel":"4","regionName":"环被社区","childRegionCodes":null},{"id":10,"regionCode":"0003103","parentRegionCode":"00031","regionLevel":"4","regionName":"市中街道","childRegionCodes":null}]},{"id":7,"regionCode":"00032","parentRegionCode":"0003","regionLevel":"3","regionName":"万山区","childRegionCodes":null}]}]}]

 

然后将这些数据转换为按照指定的层级关系展示如下图,可以去这个网址进行处理 https://jsoncrack.com/editor

递归调用的实际应用

  写递归代码的注意事项:1.方法一定要有出口,否则就是死递归;.2.方法自己调用自己。.3.一般用于这种存在

子父级关系的数据进行处理,五级以内。如果是层级非常多的情况,则可以考虑使用两个接口来实现这个功能,一个是查询

前面几级机构数据的接口;一个是根据父机构编码查询子机构编码的接口,通过这种方式也能够实现功能。有更好建议的小伙伴,

欢迎留言讨论。