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

eup*_*a83 62 java directory performance file

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

我知道标准方法:

new File(<directory path>).listFiles().length
Run Code Online (Sandbox Code Playgroud)

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

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

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

Var*_*han 80

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

new File(<directory path>).list().length
Run Code Online (Sandbox Code Playgroud)

可能是你最好的选择.


sup*_*bob 29

从Java 8开始,您可以在三行中完成:

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

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

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

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

纵观JDK8源,Files.list暴露了一个,它使用IterableFiles.newDirectoryStream委托给FileSystemProvider.newDirectoryStream.

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

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

现在,我们来看看计数机制.

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

这种方法的好处它不会将数组加载到内存中,因为条件将由底层(Filesystem)API读取,迭代器将对它们进行计数.

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

  • Java 8`Files.list()`抛出`IOException`; `File`类的`list()`方法不会抛出任何异常. (2认同)

Mic*_*ers 16

不幸的是,我认为这已经是最好的方法了(尽管list()listFiles()不构建File对象稍好一些).


Mar*_*amb 12

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

在打扰这样的事情之前,我强烈建议您创建一个包含大量文件的目录,并查看list().length是否确实需要太长时间.正如这位博主所说的那样,你可能不想为此感到沮丧.

我可能会自己去找Varkhan的答案.


mat*_*scb 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;
}
Run Code Online (Sandbox Code Playgroud)

接口文档DirectoryStream也有一些很好的例子.