【问题标题】:How to check if a directory is empty in Java如何检查Java中的目录是否为空
【发布时间】:2011-05-08 20:19:34
【问题描述】:

我想检查 Java 中的目录是否为空。但是有可能该目录中有很多文件,所以如果可能的话,我想在不查询其文件列表的情况下这样做。

【问题讨论】:

  • list() 在许多文件上可能会很慢。
  • 在扫描空目录时,我发现 .list() 占用了超过 50% 的执行时间。因此,如果您实际上并不需要文件列表,则寻找替代方案可以显着加快处理速度。
  • 通过极其缓慢的网络(通过 UNC 路径),读取包含 10,000 个子目录的目录列表曾经占用了一个程序,我的任务是维护大约 30 分钟。然后,该程序尝试读取针对数据库交叉索引的每个目录的内容,导致运行时间为 8 小时。 list() 确实非常很慢。翻转逻辑后(先查询数据库),运行时间下降到 8 分钟左右。

标签: java file-io


【解决方案1】:

使用 JDK7,您可以使用 Files.newDirectoryStream 打开目录,然后使用迭代器的 hasNext() 方法测试是否有任何文件需要迭代(不要忘记关闭流)。与 java.io.File 列表方法相比,这对于大型目录或目录位于远程文件系统上的位置应该更好。

例子:

private static boolean isDirEmpty(final Path directory) throws IOException {
    try(DirectoryStream<Path> dirStream = Files.newDirectoryStream(directory)) {
        return !dirStream.iterator().hasNext();
    }
}

【讨论】:

  • 如果在检查后想要遍历 dirStream 怎么办?我试过: for (Path artifact : dirStream) { 但是已经获得了一个迭代器 - 异常
  • @garyee,您可以将迭代器存储在局部变量中,然后按照答案中的说明执行hasNext() 检查,否则在迭代器上使用while 循环。
  • 您说don't forget to close the stream 但在示例中您没有关闭流? :)
  • @jonr,他在示例中使用了 try-with-resources,因此在退出块时会自动关闭流。
  • 问题是在 Windows 上有时会默认创建一个 desktop.ini 文件。该方法不仅仅检查该文件。有人可能会争辩说,这个目录并不是真正的空,而是用户创建的内容是空的。还是我错过了什么?
【解决方案2】:
File parentDir =  file.getParentFile();
if(parentDir.isDirectory() && parentDir.list().length == 0) {
    LOGGER.info("Directory is empty");
} else {
    LOGGER.info("Directory is not empty");
}

【讨论】:

  • "不查询其文件列表"
【解决方案3】:
if(!Files.list(Paths.get(directory)).findAny().isPresent()){
    Files.delete(Paths.get(directory));
 }

作为 Files.list 返回一个延迟填充的流,它将解决与执行时间相关的问题。

【讨论】:

    【解决方案4】:

    考虑到java.io.Filesource code,list() 方法会:

        public java.lang.String[] list() {
        ...
            byte[][] implList = listImpl(bs);
            if (implList == null) {
               // empty list
               return new String[0];
            }     
         ...
         }
    
         private synchronized static native byte[][] listImpl(byte[] path);
    

    它调用一个传递字节数组的本地方法来从中获取文件。如果方法返回null,则表示目录为空。

    这意味着,他们甚至没有本地方法来检查目录是否为空而不列出文件,因此他们无法在 java 中实现检查目录是否为空.

    结果:在java中还没有实现检查目录是否为空而不列出文件。

    【讨论】:

      【解决方案5】:

      这是一个肮脏的解决方法,但你可以尝试删除它(使用delete 方法),如果删除操作失败,则目录不为空,如果成功,则为空(但你必须重新创建它,这并不整洁)。我会继续寻找更好的解决方案。

      编辑:我从 java.nio.file.Files 类中找到 walkFileTree:http://download.java.net/jdk7/docs/api/java/nio/file/Files.html#walkFileTree(java.nio.file.Path, java.nio.file.FileVisitor) 问题是这只是 Java 7。

      我已经搜索过 S.O.对于与这个问题相关的其他问题(使用为大数组分配内存的 list() 列出目录中的文件),答案总是“你不能,除非你使用 JNI”,这两者都是平台相关且丑陋。

      【讨论】:

      • 目录删除可能由于其他原因而失败 - 例如,文件权限。
      • 事实上它很丑。希望有一个 100% 的 java 解决方案可以做到这一点。仍在搜索。
      • 我希望投反对票的人明白我完全知道这是一个可怕的黑客攻击,我已经对如何做得更好进行了全面研究,没有找到任何令人满意的东西(见编辑)
      • 嗯,问题出在目录权限上——不仅仅是错误的答案类型,还有你几乎重置权限和更改元数据的事实。对于第二个,您无能为力,但至少事先检查权限(这样您就可以摆脱误报 - 在这种情况下只需执行 list() )并为新目录设置这些权限。仍然很糟糕,但好一点。
      • 我认为即使/bin/ls 也行不通,因为它会枚举整个目录以对其输出进行排序。
      【解决方案6】:
      boolean isEmptyDirectory(Path dir) throws IOException {
          try (var entries = Files.list(dir)) {
              return entries.count() == 0;
          }
      }
      

      类似于@Minnow 的解决方案(但方法调用较少),此解决方案的好处是“作为 Files.list 返回一个延迟填充的流,它将解决您的执行时间相关问题。”

      【讨论】:

      • 我猜 count 无论如何都会加载所有目录条目。 entries.findFirst().isPresent() 不是更好的解决方案吗?
      • Files.list 和 Files.newDirectoryStream 都会懒惰地迭代目录条目。因此,他们将非常有效地处理巨大的目录。但是,您的解决方案将无法正常工作。对于大型目录,这不是一个好的选择。
      【解决方案7】:

      如果您可以使用依赖于平台的代码 - 您可以尝试通过加载系统库并使用其 API 来使用实际的本机代码。

      例如,在 Windows 中,您有一个名为 FindFirstFile() 的 Win32 API 和目录名称(没有尾部反斜杠)。如果它返回 . 和 .. 以外的其他内容,则您知道该目录不为空。它不会列出所有文件,因此它比file.list() 快得多。

      Unix 上的等价物是opendir。出于您的目的,逻辑将是相同的。

      当然 - 调用本机方法在可用性和初始库加载方面是有代价的,当它将保存在 FS 查询上时,它应该被否定。

      【讨论】:

      • 考虑到绝对没有什么能阻止人们创建不包含“.”的文件。并且没有什么能阻止人们创建包含“。”的目录。 - 该解决方案不会有任何好处。虽然我确信使用原生 API 有一种或多或少有效的方法,但调用它的开销可能会更糟。根据 FS 实现,可以想象操作系统必须枚举其中的所有文件才能开始(不知道 NTFS 是如何实现的?)
      • @Voo:正确的用法是忽略. 和.. 条目。如果文件系统不允许您在不枚举所有文件的情况下获取第一个目录条目,那么就没有快速的方法来检查它是否为空,但我怀疑这样的文件系统是否会存在。
      • @Gabe 是的,不知道 NTFS 是如何实现的。您编辑的解决方案应该正是 OP 想要的,除了它是原生的 - 我假设目录必须非常大才能抵消原生调用的成本(但你已经提到过,问题是关于无论如何,大目录也很好)。尽管您应该创建一个新帖子(编辑后的帖子与原始帖子没有太多共同点;)),但我很乐意为您的帖子投票(或者您是否因为您的编辑而获得了部分功劳? )
      【解决方案8】:
           Path checkIfEmpty=Paths.get("Pathtofile");
           DirectoryStream<Path> ds = Files.newDirectoryStream(checkIfEmpty);
           Iterator files = ds.iterator();
           if(!files.hasNext())
               Files.deleteIfExists(Paths.get(checkIfEmpty.toString()));
      

      【讨论】:

      • 完成后请小心关闭 DirectoryStream(或使用 try-with-resources),否则最终会导致文件处理程序泄漏。
      【解决方案9】:

      关于如何检查目录是否为空,我也困惑了很长时间 但答案很简单 使用

      class isFileEmpty{
      public static void main(String args[]){
      File d = new File(//Path of the Directory you want to check or open);
      String path = d.getAbsolutePath().toString();
      File dir = new File(path);
      File[] files = dir.listFiles();
      if(!dir.exists()){
      System.out.Println("Directory is Empty");
      }
      else{
      for(int i =0;i<files.length;i++){
      System.out.Println(files[i])
                 }
             }
         }
      }
      

      【讨论】:

        【解决方案10】:

        我想补充一下开发者的回答。检查是否是目录不应该在一个 If 运算符中,因为你会在逻辑上出错。首先检查是否是目录,然后检查目录是否一致,因为如果转移到方法目录不存在,您将得到错误的答案。只是检查一下。应该是这样的:

        if (file.isDirectory()) {
            if( file.list().length == 0) {
                logger.info("Directory is empty");
                return false;
            } else {
                logger.info("Directory is not empty");
                return true;
            }
        } else {
            return false;
        }
        

        【讨论】:

          猜你喜欢
          • 2018-12-23
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2019-11-01
          • 2011-11-21
          • 2018-11-20
          • 2015-05-02
          相关资源
          最近更新 更多