第八十一讲 递归

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/yerenyuan_pku/article/details/80088044

递归是一种编程思想,对于我这种愚人来说,理解起来实在是够费劲的,可以这样说,至今为止,我依然不懂递归,也只是对它有一点粗略的、表面的了解,实在是惭愧!

递归的概述

递归就是函数自身调用自身,即函数内部又使用到了该函数功能。虽然知道是这么一个意思,但一写代码遇到递归就懵逼了,所以这里我就举几个例子来详述一下它。

递归的应用之一—— 遍历指定目录下的内容(包含子目录中的内容)

现有这样一个需求:列出指定目录下的文件或者文件夹,包含子目录中的内容。也就是列出指定目录下的所有内容。

分析:因为目录中还有目录,只要使用同一个列出目录功能的函数完成即可。在列出过程中出现的还是目录的话,还可以再次调用本功能。也就是函数自身调用自身,这种表现形式或者编程手法,称为递归。

首先定义一个列出目录功能的递归函数,如下:

public static void getAllFiles(File dir) {
    System.out.println("dir : " + dir);
    //1. 获取该目录的文件对象数组。
    File[] files = dir.listFiles();

    //2. 对数组进行遍历。
    for (File file : files) {

        if (file.isDirectory()) {
            getAllFiles(file);
        } else {
            System.out.println("file : " + file);
        }

    }
}

然后调用以上递归函数,调用代码如下:

public static void main(String[] args) {
    /*
     * 遍历指定目录下的内容(包含子目录中的内容)。
     * 
     * 递归:函数自身调用自身。函数内部又使用到了该函数的功能。
     * 什么时候使用?
     * 功能被重复使用,但是每次该功能使用的参与运算的数据不同时,可以考虑递归方式来解决!
     */
    File dir = new File("F:\\Java\\JavaSE_code");
    getAllFiles(dir);
}

运行结果截图如下:
这里写图片描述
最后,我们似乎可以知道了递归什么时候使用了?——功能被重复使用,但是每次该功能使用参与运算的数据不同时,可以考虑递归方式解决。

递归的应用之二—— 求和

这里,直接给出示例代码。

public class RecursionDemo {

    public static void main(String[] args) {

        /*
         * 递归:函数自身调用自身。函数内部又使用到了该函数的功能。
         * 什么时候使用?
         * 功能被重复使用,但是每次该功能使用的参与运算的数据不同时,可以考虑递归方式来解决!
         * 
         * 使用时,一定要定义条件。
         * 注意递归次数过多,会出现栈内存溢出(java.lang.StackOverflowError)。
         */
        int sum = getSum(60000);
        System.out.println("sum = " + sum);
    }

    public static int getSum(int num) {
        if (num == 1) {
            return 1;
        }
        return num + getSum(num - 1);
    }

}

运行结果截图如下:
这里写图片描述
从以上两个例子我们可以得出如下结论:

递归要注意:
1. 限定条件。
2. 要注意递归的次数,尽量避免内存溢出(java.lang.StackOverflowError)。

递归的应用之三—— 递归删除带内容的目录

现在我们来思考一个问题——删除一个带内容的目录的过程是如何进行的呢?

分析:在Windows中,删除目录从里面往外删除的,既然是从里往外删除,就需要用到递归了。

这里,就直接给出程序代码了,如下:

import java.io.*;

public class RemoveDir {
    public static void main(String[] args) {
        File dir = new File("f:\\test");
        removeDir(dir);
    }

    public static void removeDir(File dir) {
        File[] files = dir.listFiles();
        for (int x = 0; x < files.length; x++) {
            if(!files[x].isHidden() && files[x].isDirectory()) // 避开隐藏文件
                removeDir(files[x]);
            else
                System.out.println(files[x].toString()+":-file-:"+files[x].delete());
        }
        System.out.println(dir+"::dir::"+dir.delete());
    }
}

运行结果截图如下:
这里写图片描述
此处须注意Java删除时是不走回车站的,除此之外,还须注意两点,如下:

  1. 隐藏目录——无法访问就不能删除,返回的files为空,会导致空指针异常。
  2. 系统中的有些文件虽然看上去是一个文件,其实是一个目录,或反之。

递归的应用之四——获取一个想要的指定文件的集合,例如获取F:\Java\JavaSE_code目录下(包含子目录)的所有的.java的文件对象,并存储到集合中

现有这样一个需求:获取一个想要的指定文件的集合,例如获取F:\Java\java_bxd目录下(包含子目录)的所有的.java的文件对象,并存储到集合中。
我的分析如下:

  1. 对指定的目录进行递归。
  2. 在递归的过程中需要过滤器,获取指定过滤器条件的.java文件对象。
  3. 将满足指定过滤器条件的.java文件对象都添加到集合中。

于是,首先编写一个对指定的目录进行递归的函数,可写成如下,以供参考。

/**
 * 对指定的目录进行递归。
 * 
 * 注意:多级目录下,都要用到相同的集合和过滤器,那么不要在递归方法中定义,而是不断地进行传递。
 * 
 * @param dir 需要遍历的目录。
 * @param list 用于存储符合条件的对象。
 * @param filter 接收指定的过滤器。
 */
public static void getFileList(File dir, List<File> list, FileFilter filter) {
    //1. 通过listFiles()方法,获取dir当前下的所有的文件和文件夹对象。
    File[] files = dir.listFiles();
    //2. 遍历数组。
    for (File file : files) {
        //3. 判断是否是文件,如果是,递归,如果不是,那就是文件,就需要对文件进行过滤。
        if (file.isDirectory()) {
            getFileList(file, list, filter);
        } else {
            //4. 通过过滤器对文件进行过滤。
            if (filter.accept(file)) {
                list.add(file);
            }
        }
    }
}

在编写该函数时一定要注意:多级目录下都要用到相同的集合和过滤器,那么不要在递归方法中定义,而是不断地进行传递
然后我们再定义一个获取指定过滤器条件的文件的集合的函数,可写成如下,以供参考。

/**
 * 定义一个获取指定过滤器条件的文件的集合。
 */
public static List<File> fileList(File dir, String suffix) {
    //1. 定义集合。
    List<File> list = new ArrayList<File>();

    //2. 定义过滤器。
    FileFilter filter = new FileFilterBySuffix(suffix);

    getFileList(dir, list, filter);

    return list;
}

从以上函数可看出,需要用到文件过滤器,所以我们还需要定义一个文件过滤器,这里我设计了一个——FileFilterBySuffix.java,其内容如下:

public class FileFilterBySuffix implements FileFilter {

    private String suffix;

    public FileFilterBySuffix(String suffix) {
        super();
        this.suffix = suffix;
    }

    @Override
    public boolean accept(File pathname) {
        return pathname.getName().endsWith(suffix);
    }

}

最后,我们编写测试代码进行测试。

public static void main(String[] args) {

    File dir = new File("F:\\Java\\JavaSE_code");

    List<File> list = fileList(dir, ".java");

    for (File file : list) {
        System.out.println(file);
    }

}

此综合练习用到了比较多的知识点,做起来还蛮不错的,知识点都串联起来了。

猜你喜欢

转载自blog.csdn.net/yerenyuan_pku/article/details/80088044
今日推荐