skip to content

How do you list the contents of a directory with java.io.File, and what gotchas come with list(), listFiles(), and filters?

level: juniorimportance: should knowfreq 45%

answer

  1. list()=names, listFiles()=File[]
  2. null (not empty array) on non-dir / error
  3. Empty dir returns empty array
  4. Shallow + unordered
  5. Big/recursive → Files.newDirectoryStream / walk

basics

~20 s

Call list() to get child names as Strings, or listFiles() to get child File objects, optionally with a filter. The big gotcha is that both return null (not an empty array) if the path isn't a directory or can't be read, so you must null-check before looping.

solid answer

~40 s

On a File that is a directory, list() returns a String[] of immediate child names and listFiles() returns a File[] of immediate children; overloads accept a FilenameFilter (name-based) or FileFilter (File-based) to select entries. Key gotchas: (1) both return null — not an empty array — when the File is not a directory or an I/O error/permission problem occurs, so an unchecked for-each throws NullPointerException; always null-check. (2) Listing is shallow — it returns only immediate children, not a recursive tree. (3) Order is unspecified — do not assume alphabetical. (4) The whole result is materialized into an array, which is memory-heavy for huge directories. For large or recursive traversals, NIO.2's Files.newDirectoryStream (lazy, try-with-resources, throws on error) or Files.walk / Files.list (Stream-based) are better choices.

code

java · 23 lines
java
import java.io.File;
import java.io.FileFilter;

File dir = new File("/var/log");

// WRONG: NPE if dir isn't a directory or can't be read (returns null, not [])
// for (File f : dir.listFiles()) { ... }

// RIGHT: null-check first
File[] children = dir.listFiles();
if (children == null) {
    // not a directory, missing, or unreadable — handle it
    System.err.println("Cannot list " + dir);
} else {
    // filter to subdirectories only
    FileFilter onlyDirs = File::isDirectory;
    for (File c : dir.listFiles(onlyDirs)) {
        System.out.println(c.getName()); // shallow, order NOT guaranteed
    }
}

// Filtered names via FilenameFilter (name-based):
String[] csvs = dir.list((d, name) -> name.endsWith(".csv"));

go deeper

for a junior

Knows list()/listFiles() return children and that you must null-check the result before iterating.

for a middle

Distinguishes null vs empty, knows listing is shallow and unordered, and uses FileFilter/FilenameFilter correctly.

for a senior

Chooses Files.newDirectoryStream/walk for large or recursive traversal and handles symlink cycles and ordering explicitly.

for a principal

Weighs memory/latency of array materialization vs lazy streams at scale and sets traversal conventions (error handling, symlink policy, sorting).

### Listing a directory Given a `File` that represents a directory, you can enumerate its **immediate children**: - `String[] list()` — returns the **names** of the entries (just the file/dir names, not full paths). - `File[] listFiles()` — returns `File` objects for each entry (more useful, since you can immediately query each). - Filtered overloads: - `list(FilenameFilter filter)` — filter callback gets `(File dir, String name)`. - `listFiles(FilenameFilter filter)` and `listFiles(FileFilter filter)` — the latter gets the child `File` directly, handy for `f.isDirectory()`-style tests. ### The gotchas (in priority order) 1. **`null`, not empty, on failure — the #1 trap.** If the `File` is **not a directory**, doesn't exist, or can't be read (permissions / I/O error), `list()`/`listFiles()` return **`null`**, *not* a zero-length array. Code like `for (File c : dir.listFiles()) {...}` then throws a `NullPointerException`. **Always null-check** (or use `Optional.ofNullable(...).orElse(new File[0])`). An empty directory, by contrast, returns an **empty array** — so empty array and null mean different things. 2. **Shallow only.** These methods list **immediate children**, not the whole subtree. Recursing requires you to detect directories (`child.isDirectory()`) and call again — manual and easy to get wrong (symlink cycles!). 3. **Order is unspecified.** The JVM/OS does **not** guarantee alphabetical or any stable order. Sort explicitly (`Arrays.sort`) if you need determinism. 4. **Whole result materialized.** Everything is loaded into one array up front — for directories with millions of entries this is a memory and latency problem. 5. **`.` and `..` are excluded.** The current/parent entries are not returned, which is usually what you want. 6. **Filters run in-process.** A `FilenameFilter`/`FileFilter` is just a callback evaluated for every entry; for `FileFilter`, calling `isDirectory()` inside it is an extra syscall per child. ### NIO.2 alternatives - `Files.newDirectoryStream(path)` — returns a **lazy, closeable** `DirectoryStream<Path>`; iterate in try-with-resources, and it **throws** `IOException` instead of returning null. Supports a glob filter: `newDirectoryStream(path, "*.csv")`. - `Files.list(path)` — a `Stream<Path>` of immediate children (also must be closed). - `Files.walk(path)` / `Files.find(...)` — lazy, recursive traversal with depth control and symlink options. These fix the null-vs-empty ambiguity, scale to large directories, and give proper error reporting. ### Rule of thumb Small, flat directory and you just want names → `listFiles()` is fine, **but null-check it**. Large, recursive, or error-sensitive → reach for `Files.newDirectoryStream` / `Files.walk`.

  • What's the difference between getting null and getting an empty array from listFiles()?
    null means the File is not a directory, doesn't exist, or couldn't be read (an error/permission case); an empty array means it IS a readable directory that simply has no entries. Treating them the same hides errors.
  • How would you list a directory lazily without materializing a huge array?
    Use Files.newDirectoryStream(path) in a try-with-resources block, or Files.list(path) as a closed Stream — both iterate lazily and throw IOException on error.

saying these in an interview costs you the question

  • for-each over listFiles() without a null check
  • Assuming the result is alphabetically ordered
  • Expecting list()/listFiles() to recurse into subdirectories
  • Confusing the null (failure) return with an empty array (empty dir)
  • Loading a huge directory fully into an array instead of streaming

context