使用尾部递归检索文件夹和子文件夹以读取java中的文件

使用尾部递归检索文件夹和子文件夹以读取java中的文件,java,recursion,java-8,filesystems,tail-recursion,Java,Recursion,Java 8,Filesystems,Tail Recursion,我使用普通的递归方法来迭代并从java中的文件夹和子文件夹中获取文件 有人能帮我把它改成尾部递归方法吗?我不明白什么是尾部递归。了解这一点对我很有帮助 public void findFiles(String filePath) throws IOException { List<File> files = Files.list(Paths.get(filePath)) .map(path -> path.toFi

我使用普通的递归方法来迭代并从java中的文件夹和子文件夹中获取文件

有人能帮我把它改成尾部递归方法吗?我不明白什么是尾部递归。了解这一点对我很有帮助

public void findFiles(String filePath) throws IOException {
    List<File> files = Files.list(Paths.get(filePath))
                            .map(path -> path.toFile())
                            .collect(Collectors.toList());

    for(File file: files) {
        if(file.isDirectory()){ 
                if(file.list().length == 0){
                        boolean isDeleted = file.delete();

                }else{
                    findFiles(file.getAbsolutePath());
                }
        }else{
            //process files
        }
    }
}
public void findFiles(字符串文件路径)引发IOException{
List files=files.List(path.get(filePath))
.map(路径->路径.toFile())
.collect(Collectors.toList());
用于(文件:文件){
如果(file.isDirectory()){
if(file.list().length==0){
布尔值isDeleted=file.delete();
}否则{
FindFile(file.getAbsolutePath());
}
}否则{
//处理文件
}
}
}
这是我的正常递归,有人能帮我写一个尾部递归吗

我尝试了一种方法,但我不确定这是否是尾部递归以及它是如何工作的

public static void findFiles(String filePath) throws IOException{
    List<File> files = Files.list(Paths.get(filePath))
                            .map(path -> path.toFile())
                            .collect(Collectors.toList());

    for(File file: files) {
        if(file.isDirectory() && file.list().length == 0){
             boolean isDeleted = file.delete();
        }else if(!file.isDirectory()){
                System.out.println("Processing files!!!" +  file.getAbsolutePath());
        }
        if(file.isDirectory()) {
                findFiles(file.getAbsolutePath());
        }
    }

}
publicstaticvoidfindfiles(stringfilepath)抛出IOException{
List files=files.List(path.get(filePath))
.map(路径->路径.toFile())
.collect(Collectors.toList());
用于(文件:文件){
if(file.isDirectory()&&file.list().length==0){
布尔值isDeleted=file.delete();
}else如果(!file.isDirectory()){
System.out.println(“处理文件!!!”+file.getAbsolutePath());
}
if(file.isDirectory()){
FindFile(file.getAbsolutePath());
}
}
}

提前感谢。

尾部递归是一种特殊的递归,在递归调用之后不做任何事情,而是返回

一些编程语言通过优化调用堆栈来利用这一点,这样,如果您有一个非常深的递归,那么不会出现堆栈溢出(除了内存和调用效率本身的提高)

经常使用的技巧是添加一个额外的累加器参数,该参数将处理任何未处理的数据。由于这可能会降低递归函数的可用性,因此它通常是单独完成的,因此对于函数的用户来说,它看起来很简单

因此在您的示例中是这样的,普通的
findFiles()
只准备递归调用,而
private
findFilesRecursive()
正在做尾部递归工作

public void findFiles(String filePath) throws IOException {
  //we use a Deque<> for Last In First Out ordering (to keep subfolders with their parent)
  Deque<Path> paths = new ArrayDeque<Path>();  
  paths.add(Paths.get(filePath);
  return findFilesRecursive(paths);  
}

private void findFilesRecursive(Deque<Path> pending) {
  if (pending.isEmpty()) {
    //base case, we are ready
    return;
  }

  Path path = pending.removeFirst();
  if (Files.isRegularFile(path)) {
    //todo: process the file

  } else {
      //it is a directory, queue its subfolders for processing
     List<Path> inside = Files.list(path).collect(Collectors.toList());
     if (inside.isEmpty() {
       Files.delete(path);
     } else {
       //we use LIFO so that subfolders get processed first
       inside.forEach(pending::addFirst);
     }
  }

  //tail recursion, we do nothing after we call it
  return findFilesRecursive(pending);  
}
public void findFiles(字符串文件路径)引发IOException{
//我们使用Deque进行后进先出排序(将子文件夹与其父文件夹保持在一起)
Deque路径=新的ArrayDeque();
path.add(path.get)(filePath);
返回findFilesRecursive(路径);
}
私有void findFilesRecursive(Deque待定){
if(pending.isEmpty()){
//基本情况下,我们准备好了
返回;
}
Path Path=pending.removeFirst();
if(Files.isRegularFile(path)){
//todo:处理该文件
}否则{
//它是一个目录,将其子文件夹排入队列进行处理
List inside=Files.List(path.collect(Collectors.toList());
if(inside.isEmpty()中){
删除(路径);
}否则{
//我们使用后进先出(LIFO),以便首先处理子文件夹
inside.forEach(挂起::addFirst);
}
}
//尾部递归,我们调用它之后什么也不做
返回findFileRecursive(待定);
}
请注意,Java()没有利用尾部递归,其他编程语言如Scala和Kotlin也有


旁注,
路径
通常比旧的
文件
更强大,在您的情况下,您不需要将
路径
更改为
文件

当递归调用是函数执行的最后一件事时,递归函数是尾部递归的。@CommonMan-我在谷歌上搜索过,每个人都这么说。在我的情况下,阅读目录和子目录中的文件,否则是调用我的函数的正确位置吗?这就是我感到困惑的地方。用我厌倦的内容更新了帖子。你能帮我吗?如果
findFiles(file.getAbsolutePath())
出现在循环的最后,然后是尾部递归,但我看到您在循环中使用了另外两个,所以它还不是尾部递归不一定是最后一件事,它在一个循环中,因此在它返回后可能还有其他工作要做。我必须检查文件上次修改的时间并删除该文件。因此,在这种情况下,我必须更改文件的路径,对吗?不是真的。只需执行
Files.getLastModifiedTime(path)
Files.delete(path)
。太好了!!谢谢你的帮助。但是因为Java没有利用尾部递归,所以用这种方式重写它是没有意义的,除非我们也重写它来使用循环而不是递归,递归现在很简单。顺便说一句,我会使用
ArrayDeque
,它消耗的内存要少得多。@jbx不要混淆
ArrayDeque
使用
ArrayList
。一个具体的区别是
ArrayQue
可以从前面和从后面一样快地删除(否则,以
Deque
的形式提供它是毫无意义的)。调整大小不涉及深度复制,只需复制一个引用数组,即使有可能调整大小,它也可以“固定摊销时间成本”用于添加一个元素(两端)和净性能,在大多数情况下仍优于
LinkedList