遞歸調用的實際應用
一、業務場景
項目開發中,一般是不推薦使用遞歸調用的,因為遞歸調用很佔用記憶體,並且一個不留神就可能變成死遞歸,
整個項目可能都會因為這個遞歸調用而掛掉,造成非常嚴重的後果。典型案例就是可以在電腦上面遞歸調用創建
文件夾,會直接損壞電腦上的硬碟。以前親自見到過好奇心重的人干這事,結果直接讓某個磁碟廢掉。所以遞歸的
程式碼一般都會慎用,能不用就不用。
二、需求分析
今天寫的這篇日誌主要是針對特殊場景下使用遞歸來解決問題。比如機構數據,以貴州省的數據為例,下面有
貴陽市,銅仁市;貴陽市下面有南明區,雲岩區。銅仁市下面有碧江區,萬山區。碧江區下面又有北門社區,環北社區。
機構是分層級的,存在子父級關係,比如父一級的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.一般針對這種存在
子父級關係的數據處理,五級以內。如果是層級非常多的情況,則可以考慮使用兩個介面來實現這個功能,一個是查詢前面幾級
機構數據的介面;一個是根據父機構編碼查詢子機構編碼的介面,通過這種方式也能夠實現功能。有更好建議的小夥伴,歡迎留言
探路。