List Files in a Directory in Java – 在Java中列出一个目录中的文件

最后修改: 2019年 1月 31日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

In this quick tutorial, we’ll learn different ways to list files within a directory.

在这个快速教程中,我们将学习不同的方法来列出一个目录中的文件

2. Listing

2.上市

We can list all the files in a directory with the listFiles() method on the java.io.File object that refers to a directory:

我们可以用 listFiles()方法列出一个目录中的所有文件,该方法指向一个目录的java.io.File 对象。

public Set<String> listFilesUsingJavaIO(String dir) {
    return Stream.of(new File(dir).listFiles())
      .filter(file -> !file.isDirectory())
      .map(File::getName)
      .collect(Collectors.toSet());
}

As we can see, listFiles() returns an array of File objects that are the contents of the directory.

我们可以看到,listFiles()返回一个File对象的数组,这些对象是目录的内容。

We’ll create a stream from that array. Then we’ll filter away all the values that aren’t subdirectories. Finally, we’ll collect the result into a set.

我们将从该数组中创建一个流。然后我们将过滤掉所有不是子目录的值。最后,我们将把结果收集成一个集合。

Note that we chose the Set type over List. In fact, there’s no guarantee of the order in which the files are returned by listFiles().

请注意,我们选择了Set类型而不是List。事实上,我们不能保证文件被listFiles()返回的顺序。

Using the listFiles() method on the newly instantiated File warrants some caution, as it might be null. This happens when the provided directory isn’t valid. As a result, it’ll throw a NullPointerException:

在新实例化的File上使用listFiles()方法需要注意,因为它可能为空。这发生在所提供的目录无效的情况下。因此,它将抛出一个NullPointerException

assertThrows(NullPointerException.class,
        () -> listFiles.listFilesUsingJavaIO(INVALID_DIRECTORY));

Another disadvantage of using listFiles() is that it reads the whole directory at once. Consequently, it can be problematic for folders with a large number of files.

使用listFiles()的另一个缺点是,它一次就能读取整个目录。因此,对于有大量文件的文件夹来说,它可能是有问题的。

So let’s discuss an alternate way.

因此,让我们来讨论另一种方式。

3. DirectoryStream

3.目录流

Java 7 introduced an alternative to listFiles called DirectoryStream. A directory stream was created to work well with the for-each construct to iterate over a directory’s content. This means that, instead of reading everything at once, we iterate over the contents of the directory.

Java 7 引入了listFiles的替代品,称为DirectoryStream目录流的创建是为了与for-each结构很好地配合,以迭代一个目录的内容。这意味着,我们不是一次性地读取所有内容,而是迭代目录的内容。

Let’s use this to list the files of a directory:

让我们用它来列出一个目录的文件。

public Set<String> listFilesUsingDirectoryStream(String dir) throws IOException {
    Set<String> fileSet = new HashSet<>();
    try (DirectoryStream<Path> stream = Files.newDirectoryStream(Paths.get(dir))) {
        for (Path path : stream) {
            if (!Files.isDirectory(path)) {
                fileSet.add(path.getFileName()
                    .toString());
            }
        }
    }
    return fileSet;
}

Above, we let Java handle the closing of the DirectoryStream resource through the try-with-resources construct. Similarly, we return a set of files in the folder filtering away directories.

上面,我们让Java通过DirectoryStream资源的关闭try-with-resources构造来处理这个问题。同样地,我们返回文件夹中过滤掉目录的一组文件。

Despite the confusing name, DirectoryStream isn’t part of the Stream API.

尽管名字令人困惑,DirectoryStream并不是StreamAPI的一部分。

Now we’ll see how to list files using the Stream API.

现在我们来看看如何使用Stream API来列出文件。

4. Listing in Java 8

4.在Java 8中上市

Java 8 introduced a new list() method in java.nio.file.Files. The list method returns a lazily populated Stream of entries in the directory.

Java 8在list()中引入了一个新的java.nio.file.Files方法。list方法返回一个懒惰地填充的Stream目录中的条目

4.1. Using Files.list()

4.1.使用Files.list()

Let’s see a simple example:

让我们看看一个简单的例子。

public Set<String> listFilesUsingFilesList(String dir) throws IOException {
    try (Stream<Path> stream = Files.list(Paths.get(dir))) {
        return stream
          .filter(file -> !Files.isDirectory(file))
          .map(Path::getFileName)
          .map(Path::toString)
          .collect(Collectors.toSet());
    }
}

Similarly, we return a set of files contained in the folder. Although this might look similar to listFiles(), it’s different in how we get the files’ Path.

同样地,我们返回文件夹中包含的一组文件。尽管这可能看起来与listFiles()相似,但它的不同之处在于我们如何获得文件的Path

Here, the list() method returns a Stream object that lazily populates a directory’s entries. As a result, we can process large folders more efficiently.

在这里,list()方法返回一个Stream对象,该对象懒洋洋地填充了一个目录的条目。因此,我们可以更有效地处理大型文件夹。

Again, we created the stream using the try-with-resources construct to ensure that the directory resource is closed after reading the stream.

同样,我们使用try-with-resources结构来创建流,以确保目录资源在读取流之后被关闭。

4.2. Comparison With File.list()

4.2.与File.list()比较

We mustn’t confuse the list() method provided by the Files class with the list() method on the File object. The latter returns a String array of names of all the entries of the directory, both files, and directories.

我们不能把Files类提供的list()方法和File对象上的list()方法混淆。后者返回一个String数组,其中包括目录中所有条目的名称,包括文件和目录。

5. Walking

第五次步行

Other than listing files, we might want to traverse the directory to one or more levels deeper than its direct file entries. In that case, we can use walk():

除了列出文件外,我们可能想遍历目录,到比其直接文件条目更深的一个或多个层次。在这种情况下,我们可以使用walk()。

public Set<String> listFilesUsingFileWalk(String dir, int depth) throws IOException {
    try (Stream<Path> stream = Files.walk(Paths.get(dir), depth)) {
        return stream
          .filter(file -> !Files.isDirectory(file))
          .map(Path::getFileName)
          .map(Path::toString)
          .collect(Collectors.toSet());
    }
}

The walk() method traverses the directory at the depth provided as its argument. Here, we traversed the file tree and collected the names of all the files into a Set.

walk()方法以作为其参数提供的深度遍历目录。在这里,我们遍历了文件树,并将所有文件的名称收集到一个Set中。

Additionally, we might want to take some action as we iterate on each file. In that case, we can use the walkFileTree() method by providing a visitor describing the action we want to take:

此外,我们可能想在迭代每个文件时采取一些行动。在这种情况下,我们可以使用walkFileTree()方法,提供一个描述我们要采取的行动的visitor

public Set<String> listFilesUsingFileWalkAndVisitor(String dir) throws IOException {
    Set<String> fileList = new HashSet<>();
    Files.walkFileTree(Paths.get(dir), new SimpleFileVisitor<Path>() {
        @Override
        public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
            if (!Files.isDirectory(file)) {
                fileList.add(file.getFileName().toString());
            }
            return FileVisitResult.CONTINUE;
        }
    });
    return fileList;
}

This method comes in handy when we want to do additional reading, moving, or deleting files as we go.

当我们想进行额外的阅读、移动或删除文件时,这种方法就会很方便。

The walk() and walkFileTree() methods won’t throw a NullPointerException if we try to pass in a valid file instead of a directory. In fact, the Stream guarantees to return at least one element, the provided file itself:

如果我们试图传入一个有效的文件而不是一个目录,walk()walkFileTree()方法不会抛出NullPointerException。事实上,Stream保证至少返回一个元素,即提供的文件本身。

Set<String> expectedFileSet = Collections.singleton("test.xml");
String filePathString = "src/test/resources/listFilesUnitTestFolder/test.xml";
assertEquals(expectedFileSet, listFiles.listFilesUsingFileWalk(filePathString, DEPTH));

6. Conclusion

6.结论

In this brief article, we explored different ways to list files within a directory.

在这篇简短的文章中,我们探讨了在一个目录中列出文件的不同方法。

First, we used listFiles() to get all the contents of the folder. Then we used DirectoryStream to lazy load the directory’s content. We also used the list() method introduced with Java 8.

首先,我们使用listFiles()来获取文件夹的所有内容。然后我们使用DirectoryStream来偷懒加载目录的内容。我们还使用了Java 8引入的list()方法。

Finally, we demonstrated the walk() and walkFileTree() methods for working with the file tree.

最后,我们演示了用于处理文件树的walk()walkFileTree()方法。

As always, the full source code of the examples is available over on GitHub.

一如既往,这些示例的完整源代码可在GitHub上获得over