在Java中递归列出文件

342

如何在Java中递归列出目录下的所有文件?框架是否提供任何实用工具?

我看到了很多hacky实现,但都不是来自框架或nio


3
我刚刚完成了Test Results,其中提供了许多答案的性能测试。毫不奇怪,所有基于NIO的答案表现最佳。commons-io答案明显是最差的表现者,运行时间是其他答案的两倍以上。 - Brett Ryan
2
Java8:Files.walk? - Benj
30个回答

468

Java 8提供了一个很好的流来处理树中的所有文件。

try (Stream<Path> stream = Files.walk(Paths.get(path))) {
    stream.filter(Files::isRegularFile)
          .forEach(System.out::println);
}

这提供了一种自然的遍历文件的方式。由于它是一个流,您可以对结果进行所有漂亮的流操作,例如限制、分组、映射、提前退出等。

更新:我可能需要指出,还有一个Files.find方法,它接受一个BiPredicate,如果需要检查文件属性,则可能更有效。

Files.find(Paths.get(path),
           Integer.MAX_VALUE,
           (filePath, fileAttr) -> fileAttr.isRegularFile())
        .forEach(System.out::println);

请注意,虽然JavaDoc暗示此方法可能比Files.walk更有效率,但实际上它们几乎相同。只有在您同时检索文件属性时才能观察到性能差异。最终,如果您需要根据属性进行过滤,请使用Files.find,否则请使用Files.walk,主要是因为有很多重载,更加方便。

测试:按照要求提供了许多答案的性能比较。请查看Github项目中包含的结果和测试用例


7
这是一个可以向初学者展示函数式编程魔法的例子。 - Johnny
2
这个与Java 8之前的方法相比性能如何?我的当前目录遍历速度太慢了,我正在寻找一些可以加速它的东西。 - Sridhar Sarnobat
3
@BrettRyan,我尝试了你的解决方案,但是我遇到了一个异常 Exception in thread "main" java.io.UncheckedIOException: java.nio.file.AccessDeniedException。我该如何纠正它? - Kachna
7
我该如何获得一个实际的文件清单? - dessalines
3
Files.walk看起来很好用,但在许多情况下毫无用处......如果您甚至没有访问一个文件的权限,它会抛出异常...而且你什么也得不到... - razor
显示剩余14条评论

168

46
如果你只想递归列出所有文件而不进行筛选,可以使用以下代码:FileUtils.listFiles(dir, TrueFileFilter.INSTANCE, TrueFileFilter.INSTANCE)。这里的dir是指向基础目录的一个File对象。 - andronikus
2
你可能想考虑使用listFilesAndDirs(),因为listFiles()不会返回空文件夹。 - schnatterer
1
@MikeFHay 看了一下 FileUtils 的代码,我觉得应该是 FileUtils.listFiles(dir, true, true)。使用 FileUtils.listFiles(dir, null, true) 会抛出异常,而 FileUtils.listFiles(dir, true, null) 则会列出所有文件而不会查看子目录。 - ocramot
JDK本地库怎么样?我可以很容易地实现这个,但我只会从其他地方复制黏贴。 - Christian Bongiorno
1
我正在进行一些测试,但到目前为止,这种方法似乎比使用JDK8或JDK7替代方案慢4倍。符号链接在这种方法中也证明是有问题的,特别是当它们链接到树中更高的目录时,这会导致该方法永远不返回,可以通过处理过滤器来避免这种情况,但不幸的是,即使作为文件,符号链接本身也不会被访问。 - Brett Ryan

145

// 准备运行

import java.io.File;

public class Filewalker {

    public void walk( String path ) {

        File root = new File( path );
        File[] list = root.listFiles();

        if (list == null) return;

        for ( File f : list ) {
            if ( f.isDirectory() ) {
                walk( f.getAbsolutePath() );
                System.out.println( "Dir:" + f.getAbsoluteFile() );
            }
            else {
                System.out.println( "File:" + f.getAbsoluteFile() );
            }
        }
    }

    public static void main(String[] args) {
        Filewalker fw = new Filewalker();
        fw.walk("c:\\" );
    }

}

10
请注意,对于指向路径层次结构较高的符号链接,该方法可能会导致无限循环。考虑一个带有符号链接的路径,它指向 -> . - Brett Ryan
2
这只是一个 Files.walkFileTree 的糟糕实现。我建议人们查看 FIles.walkFileTree 而不是尝试自己编写它... 它已经处理了 @BrettRyan 指出的确切问题。 - Tyler Nichols
感谢您包含import java.io.File;。许多示例都忘记包括命名空间或甚至数据类型,使示例成为探索之旅的起点。在这里,此示例已准备就绪。谢谢。 - barrypicker
路径可能因Filewalker文件所在位置而异。使用"/","./""../"分别表示根目录、当前工作目录和父目录。 - Moses Kirathe

72

Java 7 will have已经拥有Files.walkFileTree方法:

如果您提供起始点和文件访问器,它将在文件树中遍历文件时调用文件访问器上的各种方法。我们期望人们在开发递归复制、递归移动、递归删除或对每个文件设置权限或执行其他操作的递归操作时使用此方法。

现在已经有一整个Oracle关于这个问题的教程了。


它从不通知行走结束。 - Farid

29

不需要外部库。
返回一个Collection,所以在调用后你可以做任何你想做的事情。

public static Collection<File> listFileTree(File dir) {
    Set<File> fileTree = new HashSet<File>();
    if(dir==null||dir.listFiles()==null){
        return fileTree;
    }
    for (File entry : dir.listFiles()) {
        if (entry.isFile()) fileTree.add(entry);
        else fileTree.addAll(listFileTree(entry));
    }
    return fileTree;
}

19

我会选择类似以下的做法:

public void list(File file) {
    System.out.println(file.getName());
    File[] children = file.listFiles();
    for (File child : children) {
        list(child);
    }
}

System.out.println只是用来指示对文件执行某些操作。没有必要区分文件和目录,因为普通文件将简单地没有子项。


6
根据listFiles()的文档,“如果这个抽象路径名不表示一个目录,那么该方法将返回null。” - hfs
改进版public static Collection listFileTree(File dir) { if (null == dir || !dir.isDirectory()) { return Collections.emptyList(); } final Set fileTree = new HashSet(); for (File entry : Objects.requireNonNull(dir.listFiles())) { if (entry.isFile()) { fileTree.add(entry); } else { fileTree.addAll(listFileTree(entry)); } } return fileTree; } - Ben
对我来说,这是最简洁的递归答案。 - William

16

对于这种简单的遍历,我更喜欢使用队列而不是递归:

List<File> allFiles = new ArrayList<File>();
Queue<File> dirs = new LinkedList<File>();
dirs.add(new File("/start/dir/"));
while (!dirs.isEmpty()) {
  for (File f : dirs.poll().listFiles()) {
    if (f.isDirectory()) {
      dirs.add(f);
    } else if (f.isFile()) {
      allFiles.add(f);
    }
  }
}

但是你的算法无法打印缩进输出。目录和文件都混乱了。有什么解决办法吗? - Wei

13

只需要使用简单的递归自己编写:

public List<File> addFiles(List<File> files, File dir)
{
    if (files == null)
        files = new LinkedList<File>();

    if (!dir.isDirectory())
    {
        files.add(dir);
        return files;
    }

    for (File file : dir.listFiles())
        addFiles(files, file);
    return files;
}

1
请允许调用者初始化文件列表,以便它不必每次都检查其空值。如果您想创建第二个(公共)方法来创建列表,请调用此内部方法并返回完整的列表。 - helios
1
无论如何,空值检查并不是很昂贵的操作,除了方便和个人偏好之外,我认为他会明白这一点。 - pstanton
你可以稍微详细地解释一下吗? - uday

8

Java 7 中您可以使用以下类:

import java.io.IOException;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;

public class MyFileIterator extends SimpleFileVisitor<Path>
{
    public MyFileIterator(String path) throws Exception
    {
        Files.walkFileTree(Paths.get(path), this);
    }

    @Override
    public FileVisitResult visitFile(Path file,
            BasicFileAttributes attributes) throws IOException
    {
        System.out.println("File: " + file);
        return FileVisitResult.CONTINUE;
    }

    @Override
    public FileVisitResult preVisitDirectory(Path dir,
            BasicFileAttributes attributes) throws IOException
    {
        System.out.println("Dir: " + dir);
        return FileVisitResult.CONTINUE;
    }
}

8

这段代码已经可以运行

public static void main(String... args) {
    File[] files = new File("D:/").listFiles();
    if (files != null) 
       getFiles(files);
}

public static void getFiles(File[] files) {
    for (File file : files) {
        if (file.isDirectory()) {
            getFiles(file.listFiles());
        } else {
            System.out.println("File: " + file);
        }
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接