Directory Structure
BeginnerDirectory structures define how files are organised and named within a filesystem, evolving from flat single-level designs to hierarchical trees with sharing via links.
Overview
The simplest directory structure is single-level — all files in one directory, which causes naming conflicts across users. Two-level structures give each user their own directory. Tree-structured directories (used by all modern OSes) allow arbitrary nesting with absolute paths (/home/user/docs/file.txt) and relative paths (../sibling/file.txt). Acyclic-graph directories extend trees with hard and symbolic links for file sharing without duplication. General-graph directories allow cycles but require garbage collection to reclaim unreachable files. Unix uses a tree structure with acyclic-graph extensions via links. Java's NIO Path API and Files utility class map directly onto these concepts.
Path Operations with Java NIO
Java's Path API provides clean abstractions for navigating the directory hierarchy. Paths.get() parses path strings, resolve() combines paths, relativize() computes relative paths between two locations, and normalize() removes redundant . and .. elements.
import java.nio.file.*;
// Absolute path: starts from filesystem root
Path absolute = Path.of("/home/user/projects/app/src/Main.java");
System.out.println("Root: " + absolute.getRoot()); // /
System.out.println("Parent: " + absolute.getParent()); // /home/user/projects/app/src
System.out.println("Filename: " + absolute.getFileName()); // Main.java
System.out.println("Parts: " + absolute.getNameCount()); // 6
// Relative path: relative to current working directory
Path relative = Path.of("src/Main.java");
System.out.println("Is absolute: " + relative.isAbsolute()); // false
// resolve: combine a base path with another path
Path base = Path.of("/home/user/projects");
Path child = base.resolve("app/src/Main.java");
System.out.println("Resolved: " + child); // /home/user/projects/app/src/Main.java
// relativize: compute relative path from one location to another
Path from = Path.of("/home/user/projects/app");
Path to = Path.of("/home/user/projects/lib/utils.jar");
System.out.println("Relative: " + from.relativize(to)); // ../lib/utils.jar
// normalize: remove . and .. components
Path messy = Path.of("/home/user/../user/./projects//app");
System.out.println("Normalised: " + messy.normalize()); // /home/user/projects/app
// Create directories (including missing parents)
Path newDir = Path.of("/tmp/deep/nested/dir");
Files.createDirectories(newDir); // equivalent to mkdir -pWalking a Directory Tree
Files.walk() performs a depth-first traversal of a directory tree, returning a stream of Path objects. Files.walkFileTree() offers finer control via a FileVisitor with pre/post directory callbacks. Both are useful for recursive file operations like searching, copying, or computing directory sizes.
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.stream.Stream;
Path root = Path.of("/tmp/sample");
Files.createDirectories(root.resolve("a/b"));
Files.createDirectories(root.resolve("a/c"));
Files.writeString(root.resolve("a/b/file1.txt"), "hello");
Files.writeString(root.resolve("a/c/file2.txt"), "world");
// Walk: depth-first stream of all paths
try (Stream<Path> stream = Files.walk(root)) {
stream.forEach(p -> {
int depth = root.relativize(p).getNameCount();
System.out.println(" ".repeat(depth) + p.getFileName());
});
}
// Output:
// sample
// a
// b
// file1.txt
// c
// file2.txt
// Find only .txt files up to depth 5
try (Stream<Path> txtFiles = Files.walk(root, 5)
.filter(p -> p.toString().endsWith(".txt"))) {
txtFiles.forEach(System.out::println);
}
// Compute total directory size
long totalBytes;
try (Stream<Path> all = Files.walk(root)) {
totalBytes = all.filter(Files::isRegularFile)
.mapToLong(p -> { try { return Files.size(p); }
catch (Exception e) { return 0L; } })
.sum();
}
System.out.println("Total size: " + totalBytes + " bytes");Key Points to Remember
- 1Single-level directories cause naming conflicts; tree-structured directories solve this with namespacing.
- 2Absolute paths start from the root (/); relative paths are resolved from the current working directory.
- 3A directory is a file containing name-to-inode mappings — every directory has at least two entries: . (self) and .. (parent).
- 4Hard links create an acyclic-graph structure; cycles require garbage collection to detect unreachable files.
- 5Files.walk() performs a lazy depth-first traversal and must be closed (use try-with-resources) to release file handles.
- 6Path.normalize() is essential before security checks — /etc/passwd and /etc/../etc/passwd are the same file.
Interview Questions
Sign in to ask AriaWhat is the difference between an absolute path and a relative path?
Why does every directory contain . and .. entries?
How would you recursively calculate the total size of a directory tree in Java?
A web server receives a path like /files/../../etc/passwd. How do you prevent path traversal attacks using Java NIO?
Ask Aria about Directory Structure
Your personal AI tutor — ask anything about this concept
Revision Status
Personal Notes
Sign in to save personal notes for this topic.
Discussion
Sign in to join the discussion.