【问题标题】:Java: how to get all subdirs recursively?Java:如何递归获取所有子目录?
【发布时间】:2011-02-04 14:05:14
【问题描述】:

在调试 Late-hour-out-of-bound-recursive-function 之前:是否有获取子目录的命令? giveMeSubDirs(downToPath)?

// WARNING: RECURSION out of bound or too much data
public HashSet<FileObject> getAllDirs(String path) {
  HashSet<FileObject> checkedDirs = new HashSet<FileObject>();
  HashSet<FileObject> allDirs = new HashSet<FileObject>();

  String startingPath = path;

  File fileThing = new File(path);
  FileObject fileObject = new FileObject(fileThing);

  for (FileObject dir : getDirsInDir(path)) {

    // SUBDIR

    while ( !checkedDirs.contains(dir) 
        && !(getDirsInDir(dir.getFile().getParent()).size() == 0)) {

      // DO NOT CHECK TOP DIRS if any bottom dir UNCHECKED!

      while ( uncheckedDirsOnLevel(path, checkedDirs).size() > 0) { 

        while (getDirsInDir(path).size() == 0 
            || (numberOfCheckedDirsOnLevel(path, checkedDirs)==getDirsInDir(path).size())) {
          allDirs.add(new FileObject(new File(path)));
          checkedDirs.add(new FileObject(new File(path)));

          if(traverseDownOneLevel(path) == startingPath )
            return allDirs;

          //get nearer to the root
          path = traverseDownOneLevel(path);
        }
        path = giveAnUncheckedDir(path, checkedDirs);

        if ( path == "NoUnchecked.") {
          checkedDirs.add(new FileObject( (new File(path)).getParentFile() ));
          break;
        }
      }
    }
  }
  return allDirs;
}

代码总结:

  1. 尽可能深入目录树。当一个dir中没有dir时,停止,将dir放到set中,向上遍历。不要检查集合中的目录。
  2. 如果到达起始路径,请停止并返回集合。
  3. 重复步骤 1 和 2。

前提:目录结构是有限的,数据量很小。

【问题讨论】:

  • IMO 最好是自上而下然后自下而上(就像我在下面的解决方案中一样:))。这种方式更自然,您不必将路径存储在中间级别。
  • 你想做什么?您是否尝试列出所有子目录,直到达到一定级别?
  • 在标题中说:“递归”但在你的代码中不是递归的?您是在寻找递归解决方案还是非递归解决方案? FileObject的全名是什么javax.tools.FileObject
  • @Oscar:自下而上递归。不是 javax.*。请阅读警告,它不完整。

标签: java recursion filesystems subdirectory


【解决方案1】:

不,Java 标准 API 中没有这样的功能。但是在Apache commons-io中有;如果您不想将其作为库包含,也可以look at the source code

【讨论】:

    【解决方案2】:

    你可以通过以下sn-p获取所有子目录:

    File file = new File("path");
    File[] subdirs = file.listFiles(new FileFilter() {
        public boolean accept(File f) {
            return f.isDirectory();
        }
    });
    

    这只会获取直接子目录,要递归检索所有子目录,您可以编写:

    List<File> getSubdirs(File file) {
        List<File> subdirs = Arrays.asList(file.listFiles(new FileFilter() {
            public boolean accept(File f) {
                return f.isDirectory();
            }
        }));
        subdirs = new ArrayList<File>(subdirs);
    
        List<File> deepSubdirs = new ArrayList<File>();
        for(File subdir : subdirs) {
            deepSubdirs.addAll(getSubdirs(subdir)); 
        }
        subdirs.addAll(deepSubdirs);
        return subdirs;
    }
    

    【讨论】:

    • @HH Arrays.asList 显然返回一个不可变列表。因此,我们必须在其之上构造一个新的可变 ArrayList
    • 为什么使用 List 而不是 HashSet?您可以在哪里使用订单?
    • 没有任何特殊原因。但是,我不能使用 Arrays.asList - HashSet 没有等价物。 List 我相信这里的表现也更快。
    【解决方案3】:
    class DirFileFilter extends FileFilter {
      boolean accept(File pathname) {
        return pathname.isDirectory();
      }
    }
    
    DirFileFilter filter = new DirFileFilter();
    HashSet<File> files = new HashSet<File>();
    
    void rec(File root) {
      // add itself to the list
      files.put(root);
      File[] subdirs = root.list(filter);
    
      // bound of recursion: must return 
      if (subdirs.length == 0)
        return;
      else //this is the recursive case: can call itself
        for (File file : subdirs)
          rec(file);
    }
    

    【讨论】:

    【解决方案4】:

    另一个没有递归和字母顺序的版本。还使用 Set 来避免循环(在带有链接的 Unix 系统中存在问题)。

       public static Set<File> subdirs(File d) throws IOException {
            TreeSet<File> closed = new TreeSet<File>(new Comparator<File>() {
                @Override
                public int compare(File f1, File f2) {
                    return f1.toString().compareTo(f2.toString());
                }
            });
            Deque<File> open = new ArrayDeque<File>();
            open.push(d);
            closed.add(d);
            while ( ! open.isEmpty()) {
                d = open.pop();
                for (File f : d.listFiles()) {
                    if (f.isDirectory() && ! closed.contains(f)) {
                        open.push(f);
                        closed.add(f);
                    }
                }
            }
            return closed;
        }
    

    【讨论】:

    • 您实际上不应该使用 java.util.Stack 类,因为它已损坏。改用 ArrayDeque: Deque stack = new ArrayDeque();
    • @Chris:它在 pop、push 上使用 per-method 同步(基于 Vector,它也喜欢这种类型的同步)。如果您没有对该代码进行多线程处理,那将是不必要的性能损失,并且对于您需要更大粒度同步的许多多线程场景来说是不利的。 (在 Helper 的评论后查找它,因为我有同样的问题)
    【解决方案5】:

    上面的示例代码缺少“);”在声明的最后。 正确的代码应该是:

      File file = new File("path");
      File[] subdirs = file.listFiles(new FileFilter() {
          public boolean accept(File f) {
              return f.isDirectory();
          }
      });
    

    【讨论】:

      【解决方案6】:

      使用递归:

      private void getAllSubFoldersInPath(File path)
      {
          File[] files=path.listFiles();
          try {
              for(File file: files)
              {
                  if(file.isDirectory())
                  {
                      System.out.println("DIRECTORY:"+file.getCanonicalPath());
                      getAllSubFoldersInPath(file);
                  }
                  else
                  {
                      System.out.println("FILE: "+file.getCanonicalPath());   
                  }
              }
          } catch (Exception e) {
              e.printStackTrace();
          }
      }
      

      【讨论】:

        【解决方案7】:

        这是使用 Java 8 方法改进的代码。此代码将在递归基础上运行并查找目录,直到最后一个根目录。

        List<File> findAllSubdirs(File file) {
            List<File> subdirs = Arrays.asList(file.listFiles(File::isDirectory));
            subdirs = new ArrayList<File>(subdirs);
        
            List<File> deepSubdirs = new ArrayList<File>();
            for(File subdir : subdirs) {
                deepSubdirs.addAll(findAllSubdirs(subdir)); 
            }
            subdirs.addAll(deepSubdirs);
            return subdirs;
        }
        

        如果您只想要直接子目录列表,请尝试使用下面的代码行..

        List<File> subdirs = Arrays.asList(file.listFiles(File::isDirectory));
        

        【讨论】:

          【解决方案8】:
          1. 从根文件中获取所有文件作为数组(@see listFiles
          2. 通过区分文件和目录对目录进行排序 (@see isDirectory)
          3. 将第 1 步和第 2 步中的(过滤的)数组转换为列表
          4. 将所有找到的目录添加到结果列表中
          5. 对您在第 1 步中找到的每个目录文件重复该模式,结果列表会不断增加
          6. 最后,返回结果列表

          所有这些都融入了一些 lambda 魔法:

          private static List<File> getAllSubDirectories(File root, List<File> result) {
              List<File> currentSubDirs = Arrays.asList(Objects.requireNonNull(root.listFiles(File::isDirectory), "Root file has to be directory"));
              result.addAll(currentSubDirs);
              currentSubDirs.forEach(file -> getAllSubDirectories(file, result));
              return result;
          }
          

          从根文件(应该是一个目录)和一个空列表开始。

          注意:第 1 步和第 2 步可以与过滤器结合使用(@see listFiles(FileFilter filter)

          【讨论】:

          • 在 StackOverflow 上用解决方案解释答案是一种很好的做法。
          猜你喜欢
          • 1970-01-01
          • 2012-11-23
          • 2010-12-31
          • 2011-10-10
          • 2020-01-31
          • 1970-01-01
          • 2015-09-19
          • 1970-01-01
          • 2017-05-18
          相关资源
          最近更新 更多