最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java?項(xiàng)目中使用遞歸的小結(jié)

 更新時(shí)間:2024年07月02日 08:59:01   作者:CodeBlogMan  
在 Java 中,遞歸是指在方法的定義中調(diào)用自身的過(guò)程,遞歸是基于方法調(diào)用棧的原理實(shí)現(xiàn)的:當(dāng)一個(gè)方法被調(diào)用時(shí),會(huì)在調(diào)用棧中創(chuàng)建一個(gè)對(duì)應(yīng)的棧幀,包含方法的參數(shù)、局部變量和返回地址等信息,這篇文章主要介紹了Java?項(xiàng)目中對(duì)使用遞歸的理解分享,需要的朋友可以參考下

前言

筆者在最近的項(xiàng)目開(kāi)發(fā)中,遇到了兩個(gè)父子關(guān)系緊密相關(guān)的場(chǎng)景:評(píng)論樹(shù)結(jié)構(gòu)、部門樹(shù)結(jié)構(gòu)。具體的需求如:找出某條評(píng)論下的所有子評(píng)論id集合,找出某個(gè)部門下所有的子部門id集合。

在之前的項(xiàng)目開(kāi)發(fā)經(jīng)驗(yàn)中,遞歸使用得是較少的,但作為一個(gè)在數(shù)據(jù)結(jié)構(gòu)操作中遍歷樹(shù)節(jié)點(diǎn)的解決方案,我還是拿出來(lái)作為技術(shù)積累進(jìn)行記錄以及分享。

一、什么是遞歸

1.1基本概念

這里就有必要簡(jiǎn)單介紹一下關(guān)于遞歸的基本概念了。

在 Java 中,遞歸是指在方法的定義中調(diào)用自身的過(guò)程,遞歸是基于方法調(diào)用棧的原理實(shí)現(xiàn)的:當(dāng)一個(gè)方法被調(diào)用時(shí),會(huì)在調(diào)用棧中創(chuàng)建一個(gè)對(duì)應(yīng)的棧幀,包含方法的參數(shù)、局部變量和返回地址等信息。在遞歸中,方法會(huì)在自身的定義中調(diào)用自身,這會(huì)導(dǎo)致多個(gè)相同方法的棧幀依次入棧。當(dāng)滿足終止條件時(shí),遞歸開(kāi)始回溯,棧幀依次出棧,方法得以執(zhí)行完畢。

遞歸的關(guān)鍵是定義好遞歸的終止條件和遞歸調(diào)用的條件。如果沒(méi)有適當(dāng)?shù)慕K止條件或遞歸調(diào)用的條件不滿足,遞歸可能會(huì)陷入無(wú)限循環(huán),導(dǎo)致棧內(nèi)存溢出。

1.2優(yōu)缺點(diǎn)

優(yōu)點(diǎn):

  • 簡(jiǎn)化問(wèn)題:遞歸能夠?qū)?fù)雜問(wèn)題分解成更小規(guī)模的子問(wèn)題,簡(jiǎn)化了問(wèn)題的解決過(guò)程;
  • 實(shí)現(xiàn)高效算法:遞歸在某些算法中能夠?qū)崿F(xiàn)高效的解決方法,如數(shù)據(jù)結(jié)構(gòu)操作中遍歷樹(shù)節(jié)點(diǎn)等。

缺點(diǎn):

  • 棧溢出風(fēng)險(xiǎn):遞歸可能導(dǎo)致方法調(diào)用棧過(guò)深,造成棧內(nèi)存溢出;
  • 性能損耗:遞歸調(diào)用需要?jiǎng)?chuàng)建多個(gè)棧幀,對(duì)系統(tǒng)資源有一定的消耗;
  • 可讀性不高:遞歸的使用需要謹(jǐn)慎,不合理地使用可能造成代碼難以理解和調(diào)試。

1.3與迭代的區(qū)別

  • 迭代(Iteration)

    迭代常見(jiàn)于 for 循環(huán)中:比如有一個(gè)集合 A,對(duì) A 進(jìn)行 foreach,在內(nèi)部設(shè)置條件,符合條件后將集合中某個(gè)元素的值替換成別的值。

迭代示例簡(jiǎn)圖

    @Test
    public void iterationTest(){
        ArrayList<String> list = new ArrayList<>();
        list.add("計(jì)算機(jī)技術(shù)");
        list.add("土木工程");
        list.add("市場(chǎng)營(yíng)銷");
        list.forEach(val -> {
            if (val.contains("計(jì)算機(jī)")){
                log.info("迭代前的的專業(yè)名稱:{}", val);
                String str = val.replace(val, "計(jì)算機(jī)科學(xué)與技術(shù)");
                log.info("迭代后的的專業(yè)名稱:{}", str);
            }
        });
    }

結(jié)果為:

迭代結(jié)果簡(jiǎn)圖

遞歸(Recursion)

遞歸的例子會(huì)在下一小節(jié)詳細(xì)給出。

二、實(shí)際案例

下面筆者以遞歸獲取某個(gè)評(píng)論id下面所有的子級(jí)評(píng)論id為例子,向大家介紹這個(gè)遞歸的過(guò)程。

首先,這里給出一個(gè)簡(jiǎn)單的數(shù)據(jù)庫(kù)評(píng)論表的 demo,id 是主鍵id 也是評(píng)論唯一 id,parent_id 是該條評(píng)論的父評(píng)論 id,status 為1表示審核通過(guò)的狀態(tài)。

其中,我們可以簡(jiǎn)單發(fā)現(xiàn):這里21為第一層,28和29為第二層、31和32為第三層,草圖如下所示:

評(píng)論id簡(jiǎn)單層級(jí)示意圖

那么,我們?nèi)绾螌?1、28、29、31、32都放進(jìn)一個(gè)集合里返回呢?下面的代碼示例可以給你一個(gè)參考。

但是,在看代碼之前,有個(gè)問(wèn)題請(qǐng)你思考一下:

從21開(kāi)始后,遍歷的路線是21-28-29?還是21-28-31?還是21-29-32?或者是21-28-31-29-32?

下面是經(jīng)過(guò)脫敏處理后的參看代碼示例,注釋都寫得比較清楚了:

    /**
     * 這里可以看作是外部接口的調(diào)用,會(huì)得到遞歸的結(jié)果
     * @param id
     */
    private List<Integer> getIdListMethod(Integer id){
        ArrayList<Integer> idList = new ArrayList<>();
        this.getAllIdByRecursion(id, idList);
        log.info("遞歸后得到的id集合:{}", idList);
        return idList;
    }
    /**
     * 這里是遞歸的過(guò)程
     * @param id
     * @param idList
     */
    private void getAllIdByRecursion(Integer id, List<Integer> idList){
        LambdaQueryWrapper<Comment> wrapper = new LambdaQueryWrapper<>();
        //先把該id下所有的第一級(jí)子id找到
        wrapper.eq(Comment::getParentId, id).eq(Comment::getStatus, NumberUtils.INTEGER_ONE);
        List<Comment> commentList = this.list(wrapper);
        for (Comment children : commentList){
            this.getAllIdByRecursion(children.getId(), idList);
        }
        log.info("放入集合的id為:{}", id);
        idList.add(id);
    }

上面問(wèn)題的答案是:遞歸后得到的id集合:[21,28,31,29,32],原因就是:迭代會(huì)從一棵樹(shù)開(kāi)始遍歷到底,沒(méi)有元素了再?gòu)念^開(kāi)始遍歷,依次迭代,類似于深度優(yōu)先遍歷。

比如:21下面有兩個(gè)子id:28和29,那么會(huì)先走21-28-31這棵樹(shù),到底了后接著按照29-32遍歷。

三、改進(jìn)方案

我根據(jù)自己的開(kāi)發(fā)經(jīng)驗(yàn),可以從控制遞歸層數(shù)和改用 Stream 這兩種辦法來(lái)對(duì)遞歸進(jìn)行改進(jìn)。

3.1控制遞歸層數(shù)

JVM 默認(rèn)控制的遞歸最大深度限制在 1000 層,可以通過(guò)設(shè)置 JVM 參數(shù)來(lái)控制其深度,如:

java -Xss5m #表示將每個(gè)線程的棧內(nèi)存大小設(shè)置為5MB,已經(jīng)是比較大了

或者在代碼層面對(duì)遞歸的層數(shù)進(jìn)行控制:

        int depth = 0;
        //遞歸方法調(diào)用
        for (int i = 0; i < 20; i++) {
            depth++;
        }
        if (depth > 100){
            //其它操作
        }

3.1用 Stream 遍歷

核心思路是:先數(shù)據(jù)庫(kù)全量查詢(10萬(wàn)條以內(nèi)),內(nèi)存中使用 Stream 流操作、Lambda 表達(dá)式、Java 地址引用進(jìn)行篩選。

適用于數(shù)據(jù)總量不多的情況,如:部門樹(shù),部門數(shù)量一般情況是比較固定的,一個(gè)組織或者公司最多也就幾百上千個(gè)部門。

詳情可以看我這篇文章:https://www.cnblogs.com/CodeBlogMan/p/17965824

四、文章小結(jié)

筆者確實(shí)不推薦在項(xiàng)目中過(guò)度使用遞歸,但是合理使用的話也能成為解決特定問(wèn)題的一個(gè)利器,至于怎么拿捏這個(gè)度,那就要看大家的具體情況了。

Java 項(xiàng)目中對(duì)使用遞歸的理解分享到這里就結(jié)束了,文章如有不足和錯(cuò)誤,或者你有更好的解決思路,歡迎大家的指正和交流!

到此這篇關(guān)于Java 項(xiàng)目中對(duì)使用遞歸的理解分享的文章就介紹到這了,更多相關(guān)Java 項(xiàng)目中對(duì)使用遞歸的理解分享內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • eclipse中自動(dòng)生成javadoc文檔的方法

    eclipse中自動(dòng)生成javadoc文檔的方法

    這篇文章主要介紹了eclipse中自動(dòng)生成javadoc文檔的方法,是實(shí)用eclipse開(kāi)發(fā)Java程序時(shí)非常實(shí)用的技巧,對(duì)于進(jìn)行Java項(xiàng)目開(kāi)發(fā)具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2014-12-12
  • Spring實(shí)戰(zhàn)之ServletContextResource訪問(wèn)資源文件示例

    Spring實(shí)戰(zhàn)之ServletContextResource訪問(wèn)資源文件示例

    這篇文章主要介紹了Spring實(shí)戰(zhàn)之ServletContextResource訪問(wèn)資源文件,結(jié)合實(shí)例形式分析了spring使用ServletContextResource讀取與遍歷資源文件相關(guān)操作技巧,需要的朋友可以參考下
    2019-12-12
  • Java編譯錯(cuò)誤java.lang.NoSuchFieldError的解決方案詳析

    Java編譯錯(cuò)誤java.lang.NoSuchFieldError的解決方案詳析

    java.lang.NoSuchFieldError是 Java 中的一種運(yùn)行時(shí)錯(cuò)誤,這篇文章主要介紹了Java編譯錯(cuò)誤java.lang.NoSuchFieldError的解決方案,文中通過(guò)代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2025-11-11
  • 基于SpringBoot實(shí)現(xiàn)發(fā)送帶附件的郵件

    基于SpringBoot實(shí)現(xiàn)發(fā)送帶附件的郵件

    這篇文章主要介紹了基于SpringBoot實(shí)現(xiàn)發(fā)送帶附件的郵件,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-11-11
  • Java Bean與xml互相轉(zhuǎn)換的方法分析

    Java Bean與xml互相轉(zhuǎn)換的方法分析

    這篇文章主要介紹了Java Bean與xml互相轉(zhuǎn)換的方法,結(jié)合實(shí)例形式分析了java bean與xml轉(zhuǎn)換的原理與相關(guān)操作技巧,需要的朋友可以參考下
    2018-12-12
  • 實(shí)例講解Java處理PDF圖章的方法

    實(shí)例講解Java處理PDF圖章的方法

    在本篇文章里小編給大家分享了關(guān)于Java處理PDF圖章的方法,對(duì)此有需要的朋友們可以學(xué)習(xí)下。
    2019-02-02
  • zookeeper服務(wù)優(yōu)化的一些建議

    zookeeper服務(wù)優(yōu)化的一些建議

    今天小編就為大家分享一篇關(guān)于zookeeper服務(wù)優(yōu)化的一些建議,小編覺(jué)得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧
    2019-03-03
  • 詳解Spring?Boot中@PostConstruct的使用示例代碼

    詳解Spring?Boot中@PostConstruct的使用示例代碼

    在Java中,@PostConstruct是一個(gè)注解,通常用于標(biāo)記一個(gè)方法,它表示該方法在類實(shí)例化之后(通過(guò)構(gòu)造函數(shù)創(chuàng)建對(duì)象之后)立即執(zhí)行,這篇文章主要介紹了詳解Spring?Boot中@PostConstruct的使用,需要的朋友可以參考下
    2023-09-09
  • java實(shí)現(xiàn)簡(jiǎn)易的五子棋游戲

    java實(shí)現(xiàn)簡(jiǎn)易的五子棋游戲

    這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)簡(jiǎn)易的五子棋游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-06-06
  • Spring?Boot?中的?Native?SQL基本概念及使用方法

    Spring?Boot?中的?Native?SQL基本概念及使用方法

    在本文中,我們介紹了 Spring Boot 中的 Native SQL,以及如何使用 JdbcTemplate 和 NamedParameterJdbcTemplate 來(lái)執(zhí)行自定義的 SQL 查詢或更新語(yǔ)句,需要的朋友跟隨小編一起看看吧
    2023-07-07

最新評(píng)論

深水埗区| 大姚县| 双辽市| 云林县| 卢氏县| 噶尔县| 西昌市| 宣武区| 濮阳县| 罗田县| 册亨县| 改则县| 新民市| 古田县| 琼海市| 吐鲁番市| 琼结县| 连南| 文山县| 建瓯市| 庆城县| 双江| 光泽县| 临桂县| 武清区| 德惠市| 湘乡市| 蒲江县| 安平县| 南靖县| 新兴县| 旺苍县| 玉田县| 泸溪县| 厦门市| 常熟市| 泰宁县| 扬中市| 锦屏县| 醴陵市| 公主岭市|