1. Overview

Using some form of input – files, for example – is bread and butter for any developer. Java already provides several ways to interact with files, but Kotlin extends this functionality to help us traverse file trees more simply.

In this quick tutorial, we’ll learn how to list all files in a directory tree recursively using standard Kotlin methods.

2. Listing Files Recursively

Kotlin adds three extension methods that we can use to navigate file trees: walk(), walkTopDown(), and walkBottomUp().

We’ll be taking a closer look at them later, but first, it’s important to take a look at the class that they invoke: FileTreeWalk.

2.1. FileTreeWalk

Whichever of the three methods we pick, they all use FileTreeWalk underneath to do the job.

The class itself has two important characteristics. First of all, its constructors are private, so the only way we can use this class is via the aforementioned methods.

Secondly, and more importantly, it extends a Sequence<File>. That means that we have at our disposal all the iteration methods (like map and forEach) that Kotlin provides.

FileTreeWalk implements two traversal methods that allow us to iterate through all files inside a directory: top-down and bottom-up. Both of them are depth-first searches, the main difference between them being whether the directory is visited before or after the files.

It’ll be easy to understand what that difference means once we look at the examples in the next section.

2.2. The walk Method

The main file tree traversal method exposed by Kotlin is walk:

public fun File.walk(direction: FileWalkDirection = FileWalkDirection.TOP_DOWN): FileTreeWalk 
  = FileTreeWalk(this, direction)

It’s an extension method on Java’s File class and, by default, it does top-down iteration.

Let’s say we have the following file structure:

src/test/resources
├── one-in
│   ├── empty-folder
│   ├── one-in-file.md
│   └── two-in
│       ├── two-in-1.md
│       └── two-in-2.md
└── root-file.md

When we use walk to look through this structure:

File("src/test/resources").walk().forEach {
    println(it)
}

We’ll see the following output:

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

Now, let’s change the walking direction to bottom-up:

File("src/test/resources").walk(FileWalkDirection.BOTTOM_UP).forEach {
    println(it)
}

Upon execution, we’ll see a different output:

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

We can see that top-down traversal results in walk visiting directories first and files later, while bottom-up traversal results in walk visiting files first and directories later.

Passing an enum as a walk parameter is not the prettiest sight. Thankfully, we don’t have to provide FileWalkDirection by hand each time we use walk. As usual, Kotlin provides some handy shorthands.

2.3. Shorthands

Kotlin provides two handy (and more readable) shorthands for walk — walkTopDown and walkBottomUp:

File("src/test/resources").walkTopDown().forEach {
    println(it)
}

File("src/test/resources").walkBottomUp().forEach {
    println(it)
}

They do exactly what we might expect — the first runs walk in the default top-down mode, and the second runs walk in the bottom-up mode.

3. Conclusion

In this article, we’ve taken a look at how Kotlin helps us easily navigate file trees.

Article code is available, as usual, over on GitHub.

Comments are open for 30 days after publishing a post. For any issues past this date, use the Contact form on the site.