Listing Directory Contents
Fast recall
Quick revision
Core answer
Files.list(directory) for one lazy, non-recursive level, close its returned stream with try-with-resources, and use Files.walk or walkFileTree for recursive traversal. Directory order is not guaranteed unless the program sorts it.Interview-ready guide
Interview answer
Files.list(directory) returns a lazy Stream for the directory's immediate children. It lists one level only and does not enter child directories. Because the stream keeps a directory resource open, it must be used inside try-with-resources even when a terminal operation such as toList() is called.
For example, a program can list .csv files by filtering with Files.isRegularFile, checking each file name, sorting, and then collecting the result. Sorting matters when output or tests must be stable because a file system does not guarantee directory iteration order. An error during lazy iteration may appear as UncheckedIOException.
Recursive work needs a different method. Files.walk(start) provides a lazy depth-first stream, Files.find adds an attribute-aware filter, and Files.walkFileTree gives detailed control over entering directories, skipping subtrees, and handling failures. These stream-based methods also need closing.
Files.newDirectoryStream is useful for direct one-pass iteration and supports a glob or filter. The older File.listFiles() builds an array and may return null when listing fails. Choose the API from the required depth and control, then define ordering, symbolic-link behavior, error handling, and resource lifetime instead of assuming every directory can be read completely.
Listing Directory Contents
Direct Answer
Use Files.list(directory) for one lazy, non-recursive level, close its returned stream with try-with-resources, and use Files.walk or walkFileTree for recursive traversal. Directory order is not guaranteed unless the program sorts it.
Deep Dive & Architecture
Opening a directory can succeed even though reading a later entry fails. Stream-based methods wrap those later checked I/O failures in UncheckedIOException, so a terminal operation can be the point of failure. A visitor makes per-file and failed-visit handling more explicit.
Files.walk visits the starting path as well as descendants. It is depth-first and, by default, does not follow symbolic links. Following links needs an explicit option and introduces cycle detection and additional failure cases.
Collect only when a bounded result is needed. Processing entries as they arrive avoids holding a very large directory tree in memory.
Code Example
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Comparator;
class ListDirectory {
public static void main(String[] args) throws Exception {
Path directory = Files.createTempDirectory("list-demo-");
try {
Files.writeString(directory.resolve("b.csv"), "b");
Files.writeString(directory.resolve("a.csv"), "a");
try (var entries = Files.list(directory)) {
entries.filter(Files::isRegularFile)
.filter(p -> p.getFileName().toString().endsWith(".csv"))
.sorted(Comparator.comparing(Path::toString))
.forEach(p -> System.out.println(p.getFileName()));
}
} finally {
try (var entries = Files.list(directory)) {
for (Path entry : entries.toList()) Files.deleteIfExists(entry);
}
Files.deleteIfExists(directory);
}
}
}
Check your understanding
Answer these without looking back at the article.
- Why must the stream from Files.list be closed?
- Does Files.list recurse into subdirectories?
- When is walkFileTree clearer than Files.walk?
Looking for a structured roadmap?
Every module on this page is also part of the full Java backend interview track.