CoreJava Java

Java – List All Files and Subdirectories Recursively

Java – List All Files and Subdirectories Recursively

Walking a directory tree is a common task when building file management tools, log analyzers, or build scripts. Java provides multiple ways to list all files and subdirectories including nested ones. This tutorial covers the classic File API, the modern Files.walk() with Stream API (Java 7+), and how to filter by extension.

Method 1: Recursive File.listFiles() (Classic)


package com.java9r;

import java.io.File;

/**
 * Recursively lists all files and subdirectories using the classic File API.
 */
public class ListFilesRecursive {

    public static void main(String[] args) {
        String rootDir = "C:/Projects";  // change to your path
        File root = new File(rootDir);

        if (!root.exists()) {
            System.out.println("Directory not found: " + rootDir);
            return;
        }

        System.out.println("Files and directories in: " + rootDir);
        listAll(root, 0);
    }

    private static void listAll(File dir, int depth) {
        File[] items = dir.listFiles();
        if (items == null) return;

        for (File item : items) {
            // Indent based on depth for visual hierarchy
            String indent = "  ".repeat(depth);
            if (item.isDirectory()) {
                System.out.println(indent + "[DIR]  " + item.getName());
                listAll(item, depth + 1);  // recurse into subdirectory
            } else {
                System.out.printf("%s[FILE] %-30s (%,d bytes)%n",
                                  indent, item.getName(), item.length());
            }
        }
    }
}

Method 2: Files.walk() with Stream API (Modern – Java 7+)


package com.java9r;

import java.io.IOException;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;

/**
 * Modern approach using Files.walk() – cleaner, handles IOException properly.
 */
public class ListFilesStream {

    public static void main(String[] args) throws IOException {

        Path rootPath = Paths.get("C:/Projects");

        System.out.println("=== All Files and Directories ===");
        try (var stream = Files.walk(rootPath)) {
            stream.forEach(path -> {
                var depth = rootPath.relativize(path).getNameCount() - 1;
                var indent = "  ".repeat(depth);
                if (Files.isDirectory(path)) {
                    System.out.println(indent + "[DIR]  " + path.getFileName());
                } else {
                    System.out.println(indent + "[FILE] " + path.getFileName());
                }
            });
        }
    }
}

Method 3: Filter by File Extension


import java.io.IOException;
import java.nio.file.*;
import java.util.stream.Collectors;

public class FindJavaFiles {

    public static void main(String[] args) throws IOException {

        Path root = Paths.get("C:/Projects");

        // Find all .java files recursively
        System.out.println("=== All .java files ===");
        try (var stream = Files.walk(root)) {
            var javaFiles = stream
                .filter(Files::isRegularFile)
                .filter(p -> p.toString().endsWith(".java"))
                .sorted()
                .collect(Collectors.toList());

            javaFiles.forEach(p -> System.out.println("  " + p));
            System.out.println("\nTotal .java files: " + javaFiles.size());
        }

        // Count files by extension
        System.out.println("\n=== File count by extension ===");
        try (var stream = Files.walk(root)) {
            stream.filter(Files::isRegularFile)
                  .collect(Collectors.groupingBy(p -> {
                      String name = p.getFileName().toString();
                      int dot = name.lastIndexOf('.');
                      return dot >= 0 ? name.substring(dot) : "(no ext)";
                  }, Collectors.counting()))
                  .entrySet().stream()
                  .sorted(java.util.Map.Entry.comparingByValue(java.util.Comparator.reverseOrder()))
                  .forEach(e -> System.out.println("  " + e.getKey() + " : " + e.getValue()));
        }
    }
}

Method 4: Files.find() — With Attribute Filtering


import java.io.IOException;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;

public class FindLargeFiles {

    public static void main(String[] args) throws IOException {

        Path root = Paths.get("C:/Projects");
        long minSize = 100 * 1024;  // 100 KB

        System.out.println("Files larger than 100 KB:");
        try (var stream = Files.find(root, Integer.MAX_VALUE,
                (path, attrs) -> attrs.isRegularFile() && attrs.size() > minSize)) {

            stream.sorted()
                  .forEach(p -> {
                      try {
                          long size = Files.size(p);
                          System.out.printf("  %-50s %,7d KB%n",
                              p.getFileName(), size / 1024);
                      } catch (IOException e) {
                          e.printStackTrace();
                      }
                  });
        }
    }
}

Practical Example: Count Lines in All Java Files


import java.io.IOException;
import java.nio.file.*;
import java.util.concurrent.atomic.AtomicLong;

public class CountCodeLines {

    public static void main(String[] args) throws IOException {

        Path root = Paths.get("C:/Projects/MyApp/src");
        AtomicLong totalLines = new AtomicLong(0);
        AtomicLong totalFiles = new AtomicLong(0);

        try (var stream = Files.walk(root)) {
            stream.filter(Files::isRegularFile)
                  .filter(p -> p.toString().endsWith(".java"))
                  .forEach(p -> {
                      try {
                          long lines = Files.lines(p).count();
                          totalLines.addAndGet(lines);
                          totalFiles.incrementAndGet();
                          System.out.printf("  %4d lines  %s%n", lines, p.getFileName());
                      } catch (IOException e) {
                          System.err.println("Error reading: " + p);
                      }
                  });
        }

        System.out.println("\nTotal files: " + totalFiles.get());
        System.out.println("Total lines: " + totalLines.get());
    }
}

File vs Path – Which to Use?

API Available Since Recommendation
java.io.File Java 1.0 Legacy – works but less powerful
java.nio.file.Path + Files Java 7 Preferred – better error handling, Stream support
Files.walk() Java 7 Use for directory traversal
Files.find() Java 7 Use when filtering by file attributes

Summary

For listing files recursively, prefer the modern Files.walk() over the classic recursive File.listFiles() approach. Files.walk() returns a Stream, which integrates naturally with filtering, sorting, and collecting. Always close the stream with try-with-resources — leaving it open causes a file handle leak. Use Files.find() when you need to filter by file attributes like size or creation date. For large directory trees, the Stream approach is lazily evaluated and memory-efficient.

Topics: CoreJava Java
← Newer Post Older Post →