使用Java计算目录中的文件数量
如何计算使用Java的目录中的文件数量? 为了简单起见,假设目录没有任何子目录。
我知道的标准方法是:
new File(<directory path>).listFiles().length
但是,这将有效地通过目录中的所有文件,如果文件数量很大,这可能需要很长时间。 另外,我不关心目录中的实际文件,除非它们的数量大于一些固定的大数目(比如5000)。
我猜测,但目录(或Unix的情况下,它的i节点)存储在其中的文件数量? 如果我能把这个数字直接从文件系统中取出,速度会更快。 我需要在后端开始进行真正的处理之前,检查Tomcat服务器上的每个HTTP请求。 因此,速度是非常重要的。
我可以每隔一段时间运行一个守护进程来清除目录。 我知道,所以请不要给我这个解决scheme。
这可能不适合您的应用程序,但是您可以随时尝试本地调用(使用jni或jna ),或者执行特定于平台的命令并在返回到list().length之前读取输出。 在* nix上,你可以执行ls -1a | wc -l
ls -1a | wc -l
(注意 – 第一个命令是短划线,第二个是短划线-L)。 不知道什么是正确的窗口 – 也许只是一个dir
并寻找摘要。
在打扰这样的事情之前,我强烈build议你创build一个包含大量文件的目录,然后查看list()。length是否真的需要很长时间。 正如这位博客build议的,你可能不想为此付出汗水。
我可能会跟Varkhan的回答一起。
啊…没有一个简单的方法在Java中这样做的理由是文件存储抽象:一些文件系统可能没有在一个目录中的文件数量随时可用…该计数可能甚至没有任何意义(请参阅分布式P2P文件系统,将文件列表存储为链接列表的fs或数据库支持的文件系统…)。 所以是的,
new File(<directory path>).list().length
可能是你最好的select。
从Java 8开始,你可以在一行中做到这一点:
Files.list(Paths.get("your/path/here")).count();
关于5000个子节点和inode方面:
这个方法会遍历这些条目,但是Varkhanbuild议你除了使用JNI或直接系统命令调用之外可能不会做得更好,但即使如此,你也永远不能确定这些方法不会做同样的事情!
不过,我们来深入一点:
查看JDK8源代码, Files.list
公开了一个使用来自Files.newDirectoryStream
的Iterable
的stream ,该stream代表FileSystemProvider.newDirectoryStream
。
在UNIX系统上(反编译的sun.nio.fs.UnixFileSystemProvider.class
),它加载一个迭代器:使用sun.nio.fs.UnixSecureDirectoryStream
(通过遍历目录的文件锁)。
所以,有一个迭代器会循环这里的条目。
现在,让我们看看计数机制。
实际计数由Java 8stream公开的计数/总计减lessAPI执行。 从理论上讲,这个API可以在没有太多努力的情况下执行并行操作(使用multihtreading)。 然而,stream是创build与平行禁用,所以这是一个不行…
这种方法的好处是它不会在内存中加载数组,因为它们被底层(Filesystem)API读取时将被迭代器计数。
最后,对于概念上在文件系统中的信息,目录节点不需要保存它包含的文件的数量 ,它可以只包含它的子节点的列表(inode的列表)。 我不是文件系统方面的专家,但我相信UNIX文件系统就是这样工作的。 所以你不能假设有一种方法可以直接获得这些信息(即:总是可以隐藏某个子节点的列表)。
不幸的是,我相信这已经是最好的方法(尽pipelist()
比listFiles()
稍好,因为它不构造File
对象)。
既然你真的不需要总数,而且实际上想要在一定数量(在你的情况下是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
的界面文档也有一些很好的例子。
如果您的目录包含真正(> 100'000)多个文件,则这是一个(不可移植的)path:
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(); reader.close(); System.out.println(fileCount);
使用sigar应该有所帮助。 Sigar有原生的钩子来获得统计数据
new Sigar().getDirStat(dir).getTotal()
不幸的是,正如mmyers所说,File.list()与使用Java的速度差不多。 如果速度和你说的一样重要,你可能要考虑用JNI来做这个特定的操作。 然后,您可以根据您的特定情况和文件系统定制代码。