在Java中递归列出文件

342

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

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


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

7
我认为这个应该可以解决问题:
File dir = new File(dirname);
String[] files = dir.list();

这样您就有了文件和文件夹。现在使用递归,对于文件夹做同样的事情(File类有一个isDirectory()方法)。

2
“使用递归”并不是很有帮助。应该提供一个示例来说明如何使用。 - Ginger McMurray

7
在Java 8中,我们现在可以使用Files实用程序来遍历文件树。非常简单。
Files.walk(root.toPath())
      .filter(path -> !Files.isDirectory(path))
      .forEach(path -> System.out.println(path));

4

除了使用递归遍历,还可以使用基于Visitor的方法。

下面的代码使用基于Visitor的方法进行遍历。程序的输入预期为要遍历的根目录。

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

3
您可以使用以下代码递归地获取特定文件夹或目录的文件列表。
public static void main(String args[]) {

        recusiveList("D:");

    }

    public static void recursiveList(String path) {

        File f = new File(path);
        File[] fl = f.listFiles();
        for (int i = 0; i < fl.length; i++) {
            if (fl[i].isDirectory() && !fl[i].isHidden()) {
                System.out.println(fl[i].getAbsolutePath());
                recusiveList(fl[i].getAbsolutePath());
            } else {
                System.out.println(fl[i].getName());
            }
        }
    }

2
List<Path> filePaths = Files
    .find(Paths.get(dir), Integer.MAX_VALUE, (filePath, fileAttr) -> fileAttr.isRegularFile() || fileAttr.isDirectory())
    .collect(Collectors.toList());

filePaths包含文件和文件夹列表,可以进行迭代并进一步处理。


2
列出所有具有提供的扩展名的文件,可以选择扫描子文件夹(递归)。
 public static ArrayList<File> listFileTree(File dir,boolean recursive) {
        if (null == dir || !dir.isDirectory()) {
            return new ArrayList<>();
        }
        final Set<File> fileTree = new HashSet<File>();
        FileFilter fileFilter = new FileFilter() {
            private final String[] acceptedExtensions = new String[]{"jpg", "png", "webp", "jpeg"};

            @Override
            public boolean accept(File file) {
                if (file.isDirectory()) {
                    return true;
                }
                for (String extension : acceptedExtensions) {
                    if (file.getName().toLowerCase().endsWith(extension)) {
                        return true;
                    }
                }
                return false;
            }
        };
        File[] listed = dir.listFiles(fileFilter);
        if(listed!=null){
            for (File entry : listed) {
                if (entry.isFile()) {
                    fileTree.add(entry);
                } else if(recursive){
                    fileTree.addAll(listFileTree(entry,true));
                }
            }
        }
        return new ArrayList<>(fileTree);
    }

2

我为递归打印所有文件/文件名想出了这个解决方案。

最初的回答:

private static void printAllFiles(String filePath,File folder) {
    if(filePath==null) {
        return;
    }
    File[] files = folder.listFiles();
    for(File element : files) {
        if(element.isDirectory()) {
            printAllFiles(filePath,element);
        } else {
            System.out.println(" FileName "+ element.getName());
        }
    }
}

1
    private void fillFilesRecursively(File file, List<File> resultFiles) {
        if (file.isFile()) {
            resultFiles.add(file);
        } else {
            for (File child : file.listFiles()) {
                fillFilesRecursively(child, resultFiles);
            }
        }
    }

1

我的版本(当然我也可以使用Java 8中的内置walk方法;-)):

public static List<File> findFilesIn(File rootDir, Predicate<File> predicate) {
        ArrayList<File> collected = new ArrayList<>();
        walk(rootDir, predicate, collected);
        return collected;
    }

    private static void walk(File dir, Predicate<File> filterFunction, List<File> collected) {
        Stream.of(listOnlyWhenDirectory(dir))
                .forEach(file -> walk(file, filterFunction, addAndReturn(collected, file, filterFunction)));
    }

    private static File[] listOnlyWhenDirectory(File dir) {
        return dir.isDirectory() ? dir.listFiles() : new File[]{};
    }

    private static List<File> addAndReturn(List<File> files, File toAdd, Predicate<File> filterFunction) {
        if (filterFunction.test(toAdd)) {
            files.add(toAdd);
        }
        return files;
    }

1

这个被接受的答案很差,因为它可能导致资源泄漏。

Files.walkDirectoryStreams支持。

返回的流封装了一个或多个DirectoryStreams。如果需要及时处理文件系统资源,则应使用try-with-resources结构来确保在完成流操作后调用流的close方法。在关闭的流上操作会导致IllegalStateException。

必须按照其javadoc中指定的方式关闭DirectoryStream:

创建时打开DirectoryStream,并通过调用close方法关闭。关闭目录流会释放与该流相关联的任何资源。未能关闭流可能会导致资源泄漏。try-with-resources语句提供了有用的结构来确保流已关闭:

Path dir = ...
try (DirectoryStream<Path> stream = Files.newDirectoryStream(dir)) {
    for (Path entry: stream) {
        ...
    }
}

作为结果,真正的答案是:
try (Stream<Path> stream = Files.walk(Paths.get(path))) {
    // Do something with the stream.
    stream.filter(Files::isRegularFile)
          .forEach(System.out::println);
}

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