1. 概述
本文将介绍几种使用 Java 遍历文件夹中所有文件的方法,包括遍历当前目录和子文件夹的方法。
2. 使用 listFiles
我们可以通过 java.io.File
对象的listFiles()
方法来遍历指定目录下的所有文件:
public Set<String> listFilesUsingJavaIO(String dir) {
return Stream.of(new File(dir).listFiles())
.filter(file -> !file.isDirectory())
.map(File::getName)
.collect(Collectors.toSet());
}
listFiles()
返回的是一个 File
对象数组。然后我们使用Java8 Stream来进行遍历,并滤掉所有子文件夹,最终结果收集到Set中。
使用 listFiles()
方法时要注意空指针问题,如果提供的目录无效时它会抛出NullPointerException
:
assertThrows(NullPointerException.class,
() -> listFiles.listFilesUsingJavaIO(INVALID_DIRECTORY));
使用 listFiles()
的一个缺点是它会一次读取整个目录,因此对于包含文件数量较多的文件夹来说可能存在性能问题。
下面我们来讨论其他替代方案。
3. 使用 DirectoryStream
Java 7 引入了文件夹流 —— DirectoryStream 可用于替代 listFiles。DirectoryStream 可以很好地与for-each
结合以实现文件夹遍历,而不用一次性读取整个内容。
下面我们使用 DirectoryStream
来列出目录中的文件:
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;
}
同样,我们返回了文件夹中的文件列表,并过滤掉了子文件夹。我们还通过 try-with-resources 自动处理 DirectoryStream
资源的关闭。
从名字上会让人误以为 DirectoryStream
属于 Stream API,其实不是的。下面,我们学习如何使用Stream API来列出文件。
4. 使用 Files.list
Java 8 在 java.nio.file.Files
中引入了新的 list()
方法。
4.1 使用 Files.list()
下面是一个简单的例子:
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());
}
}
这段代码功能和前面一样,虽然这看起来与 listFiles()
类似,但在获取文件路径的方式上有所不同。
在这里,list()
方法返回了一个 Stream
对象,它惰性地填充了目录条目的内容。因此,我们可以更有效地处理大型文件夹。
4.2 与 File.list()
比较
我们不要混淆Files
类提供的list()
方法与File
提供的list()
方法。后者返回的是数组类型。
5. 使用 Files.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());
}
}
我们使用 walk()
深度遍历目录树,并将所有文件的名称收集到了一个Set中。
此外,如果我们需要在遍历时对文件执行一些操作,我们可以使用walkFileTree()
:
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;
}
这个方法在我们需要进行额外的读取、移动或删除文件时非常有用。
如果给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. 结论
在这篇简短的文章中,我们探讨了几种在目录中列出文件的方法。
首先,我们使用listFiles()
获取了文件夹的所有内容。然后我们使用DirectoryStream
以懒加载方式加载目录内容。我们也使用了Java 8引入的list()
方法。
最后,我们演示了walk()
和walkFileTree()
方法用于处理文件树。
如常,示例的完整源代码可以在GitHub仓库中找到。