在 Java 中递归列出文件

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2056221/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-13 03:04:27  来源:igfitidea点击:

Recursively list files in Java

javafilerecursionjava-7nio

提问by Quintin Par

How do I recursively list all files under a directory in Java? Does the framework provide any utility?

如何递归列出Java目录下的所有文件?该框架是否提供任何实用程序?

I saw a lot of hacky implementations. But none from the framework or nio

我看到了很多 hacky 实现。但没有来自框架或nio

采纳答案by Brett Ryan

Java 8 provides a nice stream to process all files in a tree.

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

Files.walk(Paths.get(path))
        .filter(Files::isRegularFile)
        .forEach(System.out::println);

This provides a natural way to traverse files. Since it's a stream you can do all nice stream operations on the result such as limit, grouping, mapping, exit early etc.

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

UPDATE: I might point out there is also Files.findwhich takes a BiPredicatethat could be more efficient if you need to check file attributes.

更新:我可能会指出还有Files.find,它采用BiPredicate,如果您需要检查文件属性,它可能会更有效。

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

Note that while the JavaDoc eludes that this method could be more efficient than Files.walkit is effectively identical, the difference in performance can be observed if you are also retrieving file attributes within your filter. In the end, if you need to filter on attributes use Files.find, otherwise use Files.walk, mostly because there are overloads and it's more convenient.

请注意,虽然 JavaDoc 没有说明此方法可能比Files.walk更有效,实际上是相同的,如果您还在过滤器中检索文件属性,则可以观察到性能差异。最后,如果你需要过滤属性使用Files.find,否则使用Files.walk,主要是因为有重载而且更方便。

TESTS: As requested I've provided a performance comparison of many of the answers. Check out the Github project which contains results and a test case.

测试:根据要求,我提供了许多答案的性能比较。查看包含结果和测试用例Github 项目

回答by Micha? Niklas

I think this should do the work:

我认为这应该可以完成工作:

File dir = new File(dirname);
String[] files = dir.list();

This way you have files and dirs. Now use recursion and do the same for dirs (Fileclass has isDirectory()method).

这样你就有了文件和目录。现在使用递归并对 dirs 执行相同的操作(File类具有isDirectory()方法)。

回答by Bozho

FileUtilshave iterateFilesand listFilesmethods. Give them a try. (from commons-io)

FileUtilsiterateFileslistFiles方法。给他们一个尝试。(来自commons-io

Edit: You can check herefor a benchmark of different approaches. It seems that the commons-io approach is slow, so pick some of the faster ones from here(if it matters)

编辑:您可以在此处查看不同方法的基准。似乎 commons-io 方法很慢,所以从这里选择一些更快的方法(如果重要的话)

回答by pstanton

just write it yourself using simple recursion:

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

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;
}

回答by Stefan Schmidt

I would go with something like:

我会选择类似的东西:

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

The System.out.println is just there to indicate to do something with the file. there is no need to differentiate between files and directories, since a normal file will simply have zero children.

System.out.println 只是用于指示对文件执行某些操作。不需要区分文件和目录,因为普通文件只会有零个子文件。

回答by stacker

// Ready to run

// 准备运行

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:\" );
    }

}

回答by yawn

Java 7 will havehas Files.walkFileTree:

Java 7的Files.walkFileTree

If you provide a starting point and a file visitor, it will invoke various methods on the file visitor as it walks through the file in the file tree. We expect people to use this if they are developing a recursive copy, a recursive move, a recursive delete, or a recursive operation that sets permissions or performs another operation on each of the files.

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

There is now an entire Oracle tutorial on this question.

现在有关于这个问题的完整Oracle 教程

回答by sateesh

Apart from the recursive traversal one can use a Visitor based approach as well.

除了递归遍历之外,还可以使用基于访问者的方法。

Below code is uses Visitor based approach for the traversal.It is expected that the input to the program is the root directory to traverse.

下面的代码是使用基于访问者的方法进行遍历。预计程序的输入是要遍历的根目录。

public interface Visitor {
    void visit(DirElement d);
    void visit(FileElement f);
}

public abstract class Element {
    protected File rootPath;
    abstract void accept(Visitor v);

    @Override
    public String toString() {
        return rootPath.getAbsolutePath();
    }
}

public class FileElement extends Element {
    FileElement(final String path) {
        rootPath = new File(path);
    }

    @Override
    void accept(final Visitor v) {
        v.visit(this);
    }
}

public class DirElement extends Element implements Iterable<Element> {
    private final List<Element> elemList;
    DirElement(final String path) {
        elemList = new ArrayList<Element>();
        rootPath = new File(path);
        for (File f : rootPath.listFiles()) {
            if (f.isDirectory()) {
                elemList.add(new DirElement(f.getAbsolutePath()));
            } else if (f.isFile()) {
                elemList.add(new FileElement(f.getAbsolutePath()));
            }
        }
    }

    @Override
    void accept(final Visitor v) {
        v.visit(this);
    }

    public Iterator<Element> iterator() {
        return elemList.iterator();
    }
}

public class ElementWalker {
    private final String rootDir;
    ElementWalker(final String dir) {
        rootDir = dir;
    }

    private void traverse() {
        Element d = new DirElement(rootDir);
        d.accept(new Walker());
    }

    public static void main(final String[] args) {
        ElementWalker t = new ElementWalker("C:\temp");
        t.traverse();
    }

    private class Walker implements Visitor {
        public void visit(final DirElement d) {
            System.out.println(d);
            for(Element e:d) {
                e.accept(this);
            }
        }

        public void visit(final FileElement f) {
            System.out.println(f);
        }
    }
}

回答by benroth

I prefer using a queue over recursion for this kind of simple traversion:

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

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);
    }
  }
}

回答by Petrucio

No external libraries needed.
Returns a Collection so you can do whatever you want with it after the call.

不需要外部库。
返回一个集合,这样你就可以在调用后对它做任何你想做的事情。

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;
}

回答by bobah

Non-recursive BFS with a single list (particular example is searching for *.eml files):

具有单个列表的非递归 BFS(特定示例是搜索 *.eml 文件):

    final FileFilter filter = new FileFilter() {
        @Override
        public boolean accept(File file) {
            return file.isDirectory() || file.getName().endsWith(".eml");
        }
    };

    // BFS recursive search
    List<File> queue = new LinkedList<File>();
    queue.addAll(Arrays.asList(dir.listFiles(filter)));

    for (ListIterator<File> itr = queue.listIterator(); itr.hasNext();) {
        File file = itr.next();
        if (file.isDirectory()) {
            itr.remove();
            for (File f: file.listFiles(filter)) itr.add(f);
        }
    }