使用Java计算目录中的文件数

时间:2009-03-26 20:25:57

标签: java performance file directory

如何使用Java计算目录中的文件数?为简单起见,我们假设该目录没有任何子目录。

我知道标准方法:

new File(<directory path>).listFiles().length

但是这将有效地遍历目录中的所有文件,如果文件数量很大,则可能需要很长时间。另外,我不关心目录中的实际文件,除非它们的数量大于某个固定的大数字(比如5000)。

我猜,但是目录(或者在Unix的情况下它的i-node)是否存储了包含在其中的文件数量?如果我可以直接从文件系统获得该数字,那将会快得多。在后端开始执行实际处理之前,我需要对Tomcat服务器上的每个HTTP请求进行此检查。因此,速度至关重要。

我可以偶尔运行一个守护进程来清除目录。我知道,所以请不要给我那个解决方案。

11 个答案:

答案 0 :(得分:79)

啊......在Java中没有简单方法的理由是文件存储抽象:一些文件系统可能没有目录中的文件数量容易获得......这个数字甚至可能没有任何意义完全(参见例如分布式,P2P文件系统,将文件列表存储为链表的fs,或数据库支持的文件系统......)。 是的,

new File(<directory path>).list().length

可能是你最好的选择。

答案 1 :(得分:26)

从Java 8开始,你可以用三行来完成:

try (Stream<Path> files = Files.list(Paths.get("your/path/here"))) {
    long count = files.count();
}

关于5000个子节点和inode方面:

这个方法将迭代条目,但正如Varkhan建议你除了玩JNI或直接系统命令调用之外你可能做得更好,但即使这样,你也永远无法确定这些方法不会做同样的事情!

但是,让我们深入研究一下:

查看JDK8源代码,Files.list公开了一个,它使用Iterable Files.newDirectoryStream代理FileSystemProvider.newDirectoryStream

在UNIX系统上(反编译sun.nio.fs.UnixFileSystemProvider.class),它会加载一个迭代器:使用sun.nio.fs.UnixSecureDirectoryStream(在遍历目录时使用文件锁)。

所以,有一个迭代器将遍历这里的条目。

现在,让我们看看计数机制。

实际计数由Java 8 streams公开的计数/求和减少API执行。从理论上讲,这个API可以毫不费力地执行并行操作(使用多线程)。但是,在禁用并行性的情况下创建流,因此不行......

这种方法的好的方面它不会在内存中加载数组,因为条目将被迭代器计数,因为它们被底层读取(Filesystem)API。

最后,对于信息,从概念上讲,在文件系统中,目录节点不需要保存它包含的文件的,它可以包含它的子节点列表(inode列表)。我不是文件系统方面的专家,但我相信UNIX文件系统就是这样的。所以你不能假设有一种方法可以直接获得这些信息(即:某些地方总会隐藏一些子节点列表)。

答案 2 :(得分:16)

不幸的是,我认为这已经是最好的方式了(尽管list()略好于listFiles(),因为它不构造File个对象。

答案 3 :(得分:12)

这可能不适合您的应用程序,但您可以尝试本机调用(使用jni或jna),或者执行特定于平台的命令并在返回list()之前读取输出。长度。在* nix上,你可以执行ls -1a | wc -l(注意 - 第一个命令是dash-one-a,第二个命令是dash-lowercase-L)。不确定窗口上的内容是什么 - 可能只是dir并查找摘要。

在烦扰这样的事情之前,我强烈建议您创建一个包含大量文件的目录,然后查看list()。length是否确实需要太长时间。正如this blogger所暗示的那样,你可能不想出汗。

我可能会自己选择Varkhan的答案。

答案 4 :(得分:6)

由于您并不真正需要总数,并且实际上想要在一定数量后执行操作(在您的情况下为5000),您可以使用java.nio.file.Files.newDirectoryStream。好处是您可以提前退出而不必通过整个目录来获取计数。

public boolean isOverMax(){
    Path dir = Paths.get("C:/foo/bar");
    int i = 1;

    try (DirectoryStream<Path> stream = Files.newDirectoryStream(dir)) {
        for (Path p : stream) {
            //larger than max files, exit
            if (++i > MAX_FILES) {
                return true;
            }
        }
    } catch (IOException ex) {
        ex.printStackTrace();
    }

    return false;
}

DirectoryStream的{​​{3}}也有一些很好的例子。

答案 5 :(得分:4)

如果你的目录中包含真正(> 100'000)的文件,那么这是一种(非便携式)方式:

String directoryPath = "a path";

// -f flag is important, because this way ls does not sort it output,
// which is way faster
String[] params = { "/bin/sh", "-c",
    "ls -f " + directoryPath + " | wc -l" };
Process process = Runtime.getRuntime().exec(params);
BufferedReader reader = new BufferedReader(new InputStreamReader(
    process.getInputStream()));
String fileCount = reader.readLine().trim() - 2; // accounting for .. and .
reader.close();
System.out.println(fileCount);

答案 6 :(得分:2)

使用sigar应该有所帮助。 Sigar有本地钩子来获取统计数据

new Sigar().getDirStat(dir).getTotal()

答案 7 :(得分:2)

这个方法对我很有效。

    // Recursive method to recover files and folders and to print the information
public static void listFiles(String directoryName) {

    File file = new File(directoryName);
    File[] fileList = file.listFiles(); // List files inside the main dir
    int j;
    String extension;
    String fileName;

    if (fileList != null) {
        for (int i = 0; i < fileList.length; i++) {
            extension = "";
            if (fileList[i].isFile()) {
                fileName = fileList[i].getName();

                if (fileName.lastIndexOf(".") != -1 && fileName.lastIndexOf(".") != 0) {
                    extension = fileName.substring(fileName.lastIndexOf(".") + 1);
                    System.out.println("THE " + fileName + "  has the extension =   " + extension);
                } else {
                    extension = "Unknown";
                    System.out.println("extension2 =    " + extension);
                }

                filesCount++;
                allStats.add(new FilePropBean(filesCount, fileList[i].getName(), fileList[i].length(), extension,
                        fileList[i].getParent()));
            } else if (fileList[i].isDirectory()) {
                filesCount++;
                extension = "";
                allStats.add(new FilePropBean(filesCount, fileList[i].getName(), fileList[i].length(), extension,
                        fileList[i].getParent()));
                listFiles(String.valueOf(fileList[i]));
            }
        }
    }
}

答案 8 :(得分:1)

不幸的是,正如mmyers所说,File.list()与使用Java的速度差不多。如果速度与您说的一样重要,您可能需要考虑使用JNI执行此特定操作。然后,您可以根据特定情况和文件系统定制代码。

答案 9 :(得分:1)

public void shouldGetTotalFilesCount() {
    Integer reduce = of(listRoots()).parallel().map(this::getFilesCount).reduce(0, ((a, b) -> a + b));
}

private int getFilesCount(File directory) {
    File[] files = directory.listFiles();
    return Objects.isNull(files) ? 1 : Stream.of(files)
            .parallel()
            .reduce(0, (Integer acc, File p) -> acc + getFilesCount(p), (a, b) -> a + b);
}

答案 10 :(得分:0)

我在春季批次中做了以下

private int getFilesCount() throws IOException {
        ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
        Resource[] resources = resolver.getResources("file:" + projectFilesFolder + "/**/input/splitFolder/*.csv");
        return resources.length;
    }