1. 概述
处理文件输入是每个开发者的日常操作。虽然 Java 已经提供了多种操作文件的方式,但 Kotlin 在此基础上进一步封装,让遍历目录树变得更简洁高效。
本文将介绍如何使用 Kotlin 标准库中的方法,递归列出目录下的所有文件。
2. 递归列出文件
Kotlin 为 File
类扩展了三个用于遍历文件树的方法:walk()
、walkTopDown()
和 walkBottomUp()
。
这些方法底层都依赖于一个核心类 —— FileTreeWalk
,我们先来深入理解它。
2.1. FileTreeWalk 类解析
无论调用上述哪个方法,最终都会创建一个 FileTreeWalk
实例来执行实际的遍历逻辑。
这个类有两个关键特性需要关注:
✅ 私有构造函数FileTreeWalk
的构造函数是私有的,因此无法直接实例化,只能通过 walk()
等扩展方法间接使用。
✅ 继承自 Sequence
它实现了 Sequence<File>
接口,这意味着我们可以像操作集合一样使用 Kotlin 提供的各种函数式操作,比如 map
、filter
、forEach
等,而且是惰性求值,不会一次性加载整个目录结构,节省内存。
此外,FileTreeWalk
支持两种遍历顺序:
- TOP_DOWN(默认):先访问目录本身,再递归进入子文件和子目录。
- BOTTOM_UP:先深度遍历到底层文件和子目录,最后才访问父目录。
两者都是基于深度优先搜索(DFS),区别仅在于目录的访问时机。
2.2. walk 方法详解
walk()
是 Kotlin 提供的核心遍历方法,定义如下:
public fun File.walk(direction: FileWalkDirection = FileWalkDirection.TOP_DOWN): FileTreeWalk
= FileTreeWalk(this, direction)
⚠️ 注意:
- 这是一个对 Java
File
类的扩展函数。 - 默认行为是
TOP_DOWN
遍历。
假设我们有以下目录结构:
src/test/resources
├── one-in
│ ├── empty-folder
│ ├── one-in-file.md
│ └── two-in
│ ├── two-in-1.md
│ └── two-in-2.md
└── root-file.md
使用默认 TOP_DOWN
模式遍历:
File("src/test/resources").walk().forEach {
println(it)
}
输出结果为:
src/test/resources
src/test/resources/root-file.md
src/test/resources/one-in
src/test/resources/one-in/one-in-file.md
src/test/resources/one-in/empty-folder
src/test/resources/one-in/two-in
src/test/resources/one-in/two-in/two-in-2.md
src/test/resources/one-in/two-in/two-in-1.md
可以看到,目录在对应文件之前被访问。
如果我们改为 BOTTOM_UP
模式:
File("src/test/resources").walk(FileWalkDirection.BOTTOM_UP).forEach {
println(it)
}
输出变为:
src/test/resources/root-file.md
src/test/resources/one-in/one-in-file.md
src/test/resources/one-in/empty-folder
src/test/resources/one-in/two-in/two-in-2.md
src/test/resources/one-in/two-in/two-in-1.md
src/test/resources/one-in/two-in
src/test/resources/one-in
src/test/resources
此时,所有文件和子目录都被优先访问,父目录最后才出现。
💡 踩坑提醒:直接传 FileWalkDirection
枚举显得冗长,影响代码可读性。好在 Kotlin 提供了更优雅的替代方案。
2.3. 更简洁的写法:walkTopDown 与 walkBottomUp
为了提升可读性,Kotlin 提供了两个封装好的快捷方法:
// 等价于 walk(FileWalkDirection.TOP_DOWN)
File("src/test/resources").walkTopDown().forEach {
println(it)
}
// 等价于 walk(FileWalkDirection.BOTTOM_UP)
File("src/test/resources").walkBottomUp().forEach {
println(it)
}
✅ 优势:
- 语义清晰,无需显式传参。
- 更符合 Kotlin 的惯用写法(idiomatic Kotlin)。
这两个方法本质上只是 walk()
的语法糖,但在实际项目中推荐优先使用它们,提高代码可维护性。
3. 总结
Kotlin 通过 walk()
及其变体,极大地简化了文件树的递归遍历操作。结合 Sequence
的惰性特性,既能高效处理大目录,又能灵活配合函数式编程风格。
文章中的示例代码已上传至 GitHub:https://github.com/Baeldung/kotlin-tutorials/tree/master/core-kotlin-modules/core-kotlin-files