当前位置: 首页 > 知识库问答 >
问题:

方法在循环中调用自身

东方栋
2023-03-14

在测试类中,有一个静态方法返回特定目录中的一组所有文件。

因为该方法调用自身来循环文件,所以我在该方法之外实例化了一组listOfFiles,以添加我找到的文件。第一次调用该方法时效果很好,但第二次调用时,listOfFiles被累加起来,由于return语句,我无法清除它。有什么想法吗?

import java.io.File;
import java.util.HashSet;

class TestClass {
    static HashSet<File> listOfFiles = new HashSet<File>();
    
    //returns all files in the directory as a set
    static HashSet<File> getAllFiles(File file) {
        if(file.isDirectory()) {
            for(File subFiles : file.listFiles()) {
                if(subFiles.isDirectory()) {
                    getAllFiles(subFiles);
                } else {
                    listOfFiles.add(subFiles);              
                    }
            }
        } else {
            listOfFiles.add(file);              
        }

//      listOfFiles.clear();
        return listOfFiles;
    }
}

public class Test {
    public static void main(String[] args) {
        TestClass.getAllFiles(new File("C:\\Users\\user\\Documents")).forEach(System.out::println);
        TestClass.getAllFiles(new File("C:\\Users\\user\\Desktop")).forEach(System.out::println);
        
    }
}

如果我编写更多的方法,或者每次调用该方法时手动清除集合,我可能会解决这个问题,但这一切似乎都不优雅和干净。

共有3个答案

史骏祥
2023-03-14
static HashSet<File> getAllFiles(File file) {
    HashSet<File> set = new HashSet<>();
    File[] files = file.listFiles();
    if (files != null) {
        for (File f : files) {
            if (f.isDirectory()) {
                set.addAll(getAllFiles(f));
            } else {
                set.add(f);
            }
        }
    }
    return set;
}
太叔志文
2023-03-14

我觉得这里有点混乱。对于递归方法,您使用的是混合方法。

您应该依赖一个只使用静态字段且没有返回语句(不推荐)的实现,或者依赖一个返回包含遇到的文件的局部变量的实现。

在第一个场景中,您不需要返回语句,因为每个方法调用总是在同一个HashSet实例上工作。静态关键字用于将成员从实例成员“转换”为类成员。具体来说,当一个字段被声明为静态时,无论创建了多少个该类的实例,都会创建该字段的单个实例。总会有一个静态字段的单个实例,其值将在每个静态和非静态上下文中共享。因此,当您在第一次主调用中更新listOfFiles的内容时,第二次调用仍然会看到第一次调用的结果,因为它们都在同一个HashSet实例上工作。

在这个实现中,您应该始终记住在每次调用getAllFiles之前清除HashSet,这绝对不是最干净也不是最好的方法。getAllFiles方法应该是独立的,并返回只属于其计算的内容。静态解决方案应该如下所示:

public class TestClass {
    static HashSet<File> listOfFiles = new HashSet<File>();

    static void getAllFiles(File file) {
        if (file.isDirectory()) {
            for (File subFiles : file.listFiles()) {
                if (subFiles.isDirectory()) {
                    getAllFiles(subFiles);
                } else {
                    listOfFiles.add(subFiles);
                }
            }
        } else {
            listOfFiles.add(file);
        }
    }

    public static void main(String[] args) {
        //The first call does not need to clear the HashSet since it is empty at the beginning
        getAllFiles(new File("C:\\Users\\user\\Documents"));
        TestClass.listOfFiles.forEach(System.out::println);
        System.out.println();

        //The second call needs to clear the results from the previous invocation
        listOfFiles.clear();
        getAllFiles(new File("C:\\Users\\user\\Desktop"));
        TestClass.listOfFiles.forEach(System.out::println);
    }
}

我推荐的第二种方法是使用本地HashSet实例,该实例将被返回并添加到调用方的HashSet中。

public class TestClass {
    static HashSet<File> listOfFiles = new HashSet<File>();

    static HashSet<File> getAllFiles(File file) {
        HashSet<File> localListOfFiles = new HashSet<File>();
        if (file.isDirectory()) {
            for (File subFiles : file.listFiles()) {
                if (subFiles.isDirectory()) {
                    //Adding to the local HashSet the HashSet returned from my inner call
                    localListOfFiles.addAll(getAllFiles(subFiles));
                } else {
                    localListOfFiles.add(subFiles);
                }
            }
        } else {
            localListOfFiles.add(file);
        }
        return localListOfFiles;
    }

    public static void main(String[] args) {
        TestClass.getAllFiles(new File("C:\\Users\\user\\Documents")).forEach(System.out::println);
        TestClass.getAllFiles(new File("C:\\Users\\user\\Desktop")).forEach(System.out::println);
    }
}
姜良哲
2023-03-14

不要使用静态字段。

而是在第一次调用时创建一个HashSet,并将其传递给所有其他调用。

最简单的方法是使用一个单独的助手方法,如下所示:

public static Set<File> getAllFiles(File file) {
    Set<File> result = new HashSet<>();
    getAllFilesImpl(file, result);
    return result;
}

private static void getAllFilesImpl(File file, Set<File> result) {
    if(file.isDirectory()) {
        for(File subFile : file.listFiles()) {
            if(subFile.isDirectory()) {
                getAllFilesImpl(subFile, result);
            } else {
                result.add(subFile);              
            }
        }
    } else {
        result.add(file);              
    }
}

注意,我还将getAllFiles()的返回类型更改为仅Set

 类似资料:
  • 假设我有一些这样的方法: 但我不想调用序列方法: 这很无聊。如果我添加一个新方法:,我需要添加手动:

  • 我正在寻找一些帮助来优化我的代码并使用更少的行数编写它。我有一个Class say A,它有几个定义的方法,如下所示: 现在我有一个单独的类,比如B和方法,编写如下 所有这些代码都运行良好。我只是想看看是否有更好的方法可以继续调用像objA这样的方法。verifyMyMethods()中的doSomethingA(),因此我可以使用更少的代码行来完成相同的工作。 我想在循环中调用方法doSomet

  • 我有一个类(称为name),它有如下不同的方法: alex(), john(), claire(), charles(), luke(), richard(), jen()等等 我需要逐一调用所有这些方法,例如: ... 等等 我尝试的是创建一个名称列表,并调用循环中的所有方法 但这种调用方法的方式正在失败。有没有办法在循环中调用同一类的多个方法?

  • 问题内容: 我正在编写一个游戏引擎,其中使用for循环迭代保存在a 中的一组对象。显然,效率非常重要,因此我想知道循环的效率。 当返回的秒。我想知道的是,每次循环在新扩展上进行迭代时,是否都调用该方法。如果是这样,这样做会更有效: ?提前致谢。 问题答案: 按照规范,这个成语 扩展成 因此,您询问的呼叫在循环初始化时仅发生一次。这是迭代器对象,其方法被重复调用。 但是,如果您真的对应用程序的性能感

  • 本文向大家介绍C#中for循环、while循环循环执行的方法,包括了C#中for循环、while循环循环执行的方法的使用技巧和注意事项,需要的朋友参考一下 先给大家介绍下C#中的循环执行for循环 在这一节练习中,我们向大家介绍一下C#中的另一种重要的循环语句,for循环。 表达式1:一般为赋值表达式,给控制变量赋初值; 表达式2:逻辑表达式,循环控制条件;当条件为真时,循环执行循环体中的语句。

  • 主要内容:优点,缺点轮循调度算法是最流行的调度算法之一,它可以在大多数操作系统中实际实现。 这是先到先得的排程先发制人的版本。 该算法着重于时间共享。 在这个算法中,每个进程都以循环方式执行。 在称为时间量的系统中定义了一定的时间片。 就绪队列中的每个进程都分配给该时间段的CPU,如果在该时间内进程的执行完成,那么进程将终止,否则进程将返回就绪队列并等待下一轮完成 执行。 优点 它可以在系统中实际实现,因为它不依赖于