遞歸調用的實際應用

一、業務場景

  項目開發中,一般是不推薦使用遞歸調用的,因為遞歸調用很佔用記憶體,並且一個不留神就可能變成死遞歸,

整個項目可能都會因為這個遞歸調用而掛掉,造成非常嚴重的後果。典型案例就是可以在電腦上面遞歸調用創建

文件夾,會直接損壞電腦上的硬碟。以前親自見到過好奇心重的人干這事,結果直接讓某個磁碟廢掉。所以遞歸的

程式碼一般都會慎用,能不用就不用。

二、需求分析

  今天寫的這篇日誌主要是針對特殊場景下使用遞歸來解決問題。比如機構數據,以貴州省的數據為例,下面有

貴陽市,銅仁市;貴陽市下面有南明區,雲岩區。銅仁市下面有碧江區,萬山區。碧江區下面又有北門社區,環北社區。

機構是分層級的,存在子父級關係,比如父一級的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’, ‘市中街道‘);

之後根據這個簡單的遞歸調用方法,來寫當前機構子機構處理的程式碼。經過反覆的修改與調試,最終完成程式碼,

測試調試通過。示例程式碼如下,

    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}]}]}]

 

然後將這些數據轉換為指定的分層級的關係如下,

  寫遞歸程式碼的注意事項:1.方法一定要有出口,否則就是死遞歸;.2.方法自己調用自己。.3.一般針對這種存在

子父級關係的數據處理,五級以內。如果是層級非常多的情況,則可以考慮使用兩個介面來實現這個功能,一個是查詢前面幾級

機構數據的介面;一個是根據父機構編碼查詢子機構編碼的介面,通過這種方式也能夠實現功能。有更好建議的小夥伴,歡迎留言

探路。