Java
Introduction to Java and a Complete Roadmap to Ace Coding Interviews with Java
A practical guide for beginners and for engineers who already code but want to interview well in Java.
Table of Contents
- Part 1 — Introduction to Java
- Part 2 — Should You Interview in Java?
- Part 3 — The Complete Roadmap
- Part 4 — The Java Interview Cheat Sheet
- Part 5 — 15 Java Gotchas That Fail Interviews
- Part 6 — Worked Examples
- Part 7 — Your Practice System
- Part 8 — Interview Day Playbook
Part 1 — Introduction to Java
What Java Actually Is
Java is a statically typed, object-oriented, compiled-and-then-interpreted programming language released by Sun Microsystems in 1995 and now stewarded by Oracle. It was designed by James Gosling's team around one central promise: write once, run anywhere.
That promise is delivered by an unusual two-step execution model:
Your code (.java)
│ javac (compiler)
▼
Bytecode (.class) ← platform-independent
│ JVM (Java Virtual Machine)
▼
Machine code for whatever CPU you're on
Most languages compile straight to machine code for one target platform. Java compiles to bytecode, an intermediate instruction set that any JVM can read. Ship one .jar file and it runs on Windows, Linux, macOS, an Android phone, or a mainframe — because each platform has its own JVM implementation doing the final translation.
JDK vs JRE vs JVM
These three acronyms confuse every beginner, and interviewers do ask. The relationship is nested:
| Component | What it is | What's inside | Who needs it |
|---|---|---|---|
| JVM | Java Virtual Machine | Bytecode interpreter, JIT compiler, garbage collector, memory manager | Anyone running Java |
| JRE | Java Runtime Environment | JVM + the standard class libraries (java.util, java.io, …) |
Anyone running Java |
| JDK | Java Development Kit | JRE + javac, javadoc, jdb, jar, and other dev tools |
Anyone writing Java |
One-line answer for interviews: JDK ⊃ JRE ⊃ JVM. You develop with the JDK, users run with the JRE, and the JVM is the engine inside both.
One more piece worth knowing: the JIT (Just-In-Time) compiler. The JVM starts by interpreting bytecode, but it watches which methods run frequently ("hot spots") and compiles those to native machine code at runtime, applying optimizations it can only know from actual runtime behavior. This is why Java starts slower than C++ but often reaches comparable steady-state throughput.
Core Characteristics
Statically and strongly typed. Every variable has a declared type, checked at compile time. int count = "hello"; won't compile. This catches a whole class of bugs before your program runs, at the cost of more typing.
Object-oriented, aggressively so. Everything except primitives lives inside a class. There are no free-floating functions.
Automatic memory management. You allocate with new; you never free. The garbage collector reclaims objects that are no longer reachable. No malloc, no free, no dangling pointers, no manual reference counting. You trade some control and predictable latency for a large reduction in memory bugs.
No pointer arithmetic. Java has references, not pointers. You cannot cast an integer to a memory address and read it. This is a deliberate safety decision.
Backward compatible to a fault. Code written in 2005 usually still compiles today. This is why enterprises trust Java with 20-year-old systems — and also why the language carries some awkward legacy (type erasure in generics, Date vs Calendar vs LocalDate).
Multithreaded from day one. Threads, locks, and a memory model were in the language from early versions, not bolted on later. The java.util.concurrent package is one of the best concurrency libraries in any language.
Your First Program, Line by Line
public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello, World!");
}
}
Every token here means something:
public— access modifier; visible from anywhere.class HelloWorld— declares a class. In older Java the filename must match:HelloWorld.java.static— belongs to the class, not to an instance. The JVM callsmainbefore any object exists, so it must be static.void— returns nothing.main(String[] args)— the exact signature the JVM looks for as an entry point.argsholds command-line arguments.System.out.println(...)—Systemis a class,outis a staticPrintStreamfield,printlnprints with a newline.
Compile and run:
javac HelloWorld.java # produces HelloWorld.class
java HelloWorld # runs it (no .class extension)
Since Java 11 you can skip the compile step for single-file programs:
java HelloWorld.java
And in Java 21+, this is legal — much friendlier for beginners:
void main() {
System.out.println("Hello, World!");
}
Java Versions You Should Know
Java releases every six months now, but only LTS (Long-Term Support) versions matter in practice.
| Version | Year | Why it matters |
|---|---|---|
| Java 8 | 2014 | The watershed release: lambdas, streams, Optional, java.time. Still everywhere in legacy systems. |
| Java 11 | 2018 | First LTS after 8. var, new String methods, HTTP client. |
| Java 17 | 2021 | Records, sealed classes, switch expressions, text blocks. The modern baseline. |
| Java 21 | 2023 | Virtual threads, pattern matching for switch, record patterns. |
| Java 25 | 2025 | Latest LTS; continued refinement of the above. |
For interviews: know Java 8 features cold (streams and lambdas come up constantly), and be aware of records and var from 17+. If you mention a feature, know which version introduced it.
Where Java Is Actually Used
- Backend services — Spring Boot dominates enterprise APIs. Banking, insurance, retail, logistics.
- Android — Kotlin is now preferred, but the Android SDK is Java-based and enormous amounts of Java remain.
- Big data — Hadoop, Spark, Kafka, Flink, Elasticsearch, Cassandra are all JVM projects.
- Trading systems — low-latency finance uses carefully tuned Java more than you'd expect.
- Enterprise tooling — Jenkins, IntelliJ, Eclipse, JIRA.
The practical implication: Java jobs are abundant and stable. They skew toward larger companies and established industries rather than early-stage startups.
Part 2 — Should You Interview in Java?
Be honest about the trade-offs before committing.
Where Java Helps You
Your data structures are already written and battle-tested. HashMap, TreeMap, PriorityQueue, ArrayDeque, LinkedHashMap — the standard library covers essentially every structure an interview needs, with predictable performance.
Type declarations document your thinking. When you write Map<String, List<Integer>> groups, the interviewer immediately sees your data model. In dynamically typed languages you have to explain it out loud.
It signals the right things for backend roles. If the job is Spring Boot, interviewing in Java shows you'll be productive on day one.
Strict compilation catches your mistakes. Type errors surface before you run, which is genuinely useful under time pressure.
Where Java Hurts You
It's verbose. Compare:
# Python
seen = {}
// Java
Map<String, Integer> seen = new HashMap<>();
Across a 40-minute interview that verbosity costs you real minutes. Mitigate it by memorizing idioms until they're muscle memory, and by using var where the type is obvious from the right side.
Primitive/wrapper friction. int vs Integer, int[] vs List<Integer>, no Map<int, int>. There's no generic way to make a heap of primitives without boxing. This is real overhead you must learn to navigate smoothly.
No tuples, no multiple returns. Returning two values means an int[], a small class, or a record. Python's return a, b has no equivalent.
Verdict: Java is a strong interview language for backend, Android, and big-data roles. If you're interviewing at a company where Java is the primary stack, use it. If you're purely optimizing for speed of expression in algorithm rounds and have no stack preference, Python is faster to write. Never learn a new language just for interviews — fluency beats terseness every time.
Part 3 — The Complete Roadmap
This roadmap has nine phases. The timeline depends on your starting point:
- Complete beginner to programming: 20–24 weeks at 10–15 hrs/week
- Know another language, new to Java: 12–14 weeks
- Know Java, weak on algorithms: 8–10 weeks (skip to Phase 4)
- Experienced, brushing up for a specific interview: 3–4 weeks (Phases 5–8 only)
Do not skip phases out of order. Attempting dynamic programming before you're fluent in recursion is the single most common reason people plateau.
Phase 0 — Setup (2–3 days)
Don't over-engineer this. Get working fast.
Install a JDK. Download Java 21 or later from Adoptium (Eclipse Temurin) — a free, well-maintained OpenJDK build. Verify:
java --version
javac --version
Install IntelliJ IDEA Community Edition. It's free and the best Java IDE by a wide margin. VS Code with the Java extension pack works too if you already live there.
Learn these four IntelliJ shortcuts. They save hours:
psvm+ Tab → generatespublic static void mainsout+ Tab → generatesSystem.out.println()Ctrl/Cmd + Alt + L→ reformat codeCtrl/Cmd + B→ jump to definition
Set up a practice repository. One Git repo, one folder per topic, one file per problem. Commit every solve. In three months this becomes your personal reference and a visible record of consistency.
Create accounts: LeetCode (primary), plus one of HackerRank or Codeforces if you like contests.
⚠️ Trap to avoid: spending a week comparing build tools. You do not need Maven or Gradle to solve algorithm problems. A single .java file with a main method is enough for months.
Phase 1 — Core Language Fundamentals (1–2 weeks)
Master the mechanics before touching algorithms.
Primitives and Types
byte b = 127; // 8-bit, -128 to 127
short s = 32000; // 16-bit
int i = 2_147_483_647; // 32-bit — your default integer
long l = 9_000_000_000L; // 64-bit — note the L suffix
float f = 3.14f; // 32-bit — note the f suffix
double d = 3.14159; // 64-bit — your default decimal
char c = 'A'; // 16-bit UTF-16 code unit
boolean flag = true; // true or false only
Memorize the boundaries: Integer.MAX_VALUE is 2,147,483,647 (≈2.1 × 10⁹) and Long.MAX_VALUE is ≈9.2 × 10¹⁸. Interview problems are designed around these limits. If a constraint says values go up to 10⁹ and you're summing an array of 10⁵ of them, the sum overflows int — you need long.
Operators, Control Flow, Methods
Cover: arithmetic and integer division (7 / 2 == 3), modulo behavior with negatives (-7 % 3 == -1 in Java), comparison and logical operators with short-circuiting, ternary, if/else, switch (both statement and expression forms), for, enhanced for, while, do-while, break/continue, labeled breaks, method declaration, overloading, and varargs.
Arrays
int[] arr = new int[5]; // [0, 0, 0, 0, 0]
int[] nums = {3, 1, 4, 1, 5}; // literal
int[][] grid = new int[3][4]; // 2D, 3 rows × 4 cols
int[][] jagged = new int[3][]; // rows sized individually
arr.length // field, not method — no ()
Arrays.sort(nums); // in-place sort
Arrays.fill(arr, -1); // fill with a value
int[] copy = Arrays.copyOf(nums, nums.length);
int[] slice = Arrays.copyOfRange(nums, 1, 4); // [1, 4) — end exclusive
Arrays.toString(nums); // for debugging
Arrays.deepToString(grid); // for 2D debugging
Strings — Critical for Interviews
Strings are immutable. Every "modification" creates a new object. This has a direct algorithmic consequence:
// ❌ O(n²) — builds a new String every iteration
String result = "";
for (char c : chars) result += c;
// ✅ O(n) — mutable buffer
StringBuilder sb = new StringBuilder();
for (char c : chars) sb.append(c);
String result = sb.toString();
That single distinction has failed more interviews than any other Java detail. Use StringBuilder for any string built in a loop.
Essential String methods: length(), charAt(i), substring(a, b) (end exclusive), indexOf, lastIndexOf, contains, startsWith, endsWith, equals, equalsIgnoreCase, compareTo, toCharArray, split, trim/strip, toLowerCase, replace, String.valueOf, String.join, isEmpty, isBlank, repeat, chars.
Exceptions
Understand checked vs unchecked, try/catch/finally, try-with-resources, and custom exceptions. Interviewers ask: "What's the difference between a checked and an unchecked exception?" Answer: checked exceptions extend Exception and must be declared or handled at compile time (IOException); unchecked extend RuntimeException and don't (NullPointerException, IllegalArgumentException).
Checkpoint before moving on: Write, without looking anything up — FizzBuzz, reverse a string in place, find the max in an array, check if a number is prime, print a triangle of stars, and count vowels in a sentence. If any of those require Googling, stay in this phase.
Phase 2 — Object-Oriented Programming (1–2 weeks)
OOP is examined directly in Java interviews, not just indirectly. Expect explicit questions.
The Four Pillars
Encapsulation — bundle data with the methods that operate on it; hide internals behind an interface.
public class BankAccount {
private double balance; // hidden state
public void deposit(double amount) {
if (amount <= 0) throw new IllegalArgumentException("Must be positive");
balance += amount; // invariant enforced here
}
public double getBalance() { return balance; }
}
The point isn't getters and setters. The point is that balance can never go negative because every path that touches it is validated.
Inheritance — extends for classes, implements for interfaces. Java allows single class inheritance but multiple interface implementation.
Polymorphism — one interface, many implementations. Distinguish:
- Compile-time (overloading): same method name, different parameter lists, resolved by the compiler.
- Runtime (overriding): subclass replaces a superclass method, resolved by the JVM via dynamic dispatch.
Abstraction — expose what, hide how, via abstract classes and interfaces.
Abstract Class vs Interface — Guaranteed Question
| Abstract class | Interface | |
|---|---|---|
| Instance fields | Yes | No (only static final constants) |
| Constructor | Yes | No |
| Method bodies | Yes | Yes, via default/static (Java 8+) |
| Multiple inheritance | No | Yes |
| Access modifiers | Any | public by default |
| Use when | Sharing state and code among close relatives | Defining a capability across unrelated types |
One-liner: an abstract class models "is a kind of"; an interface models "is capable of".
Also Cover
static vs instance members · final on variables, methods, and classes · constructors and constructor chaining (this(), super()) · the Object methods you must override together (equals and hashCode — always both, plus toString) · Comparable vs Comparator · enums (real ones with fields and methods) · nested, inner, static nested, and anonymous classes · records (Java 16+) as immutable data carriers:
record Point(int x, int y) {} // constructor, equals, hashCode, toString — all generated
The equals/hashCode Contract
This shows up in interviews and silently breaks real code:
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Point p = (Point) o;
return x == p.x && y == p.y;
}
@Override
public int hashCode() {
return Objects.hash(x, y);
}
The rule: if two objects are equals, they must have the same hashCode. Break it and your objects vanish inside HashMap and HashSet — you'll put one in and fail to find it.
Phase 3 — Collections & Streams: Your Interview Power Tools (1–2 weeks)
This phase gives the highest return per hour invested. Fluency here is the difference between finishing a problem and running out of time.
The Hierarchy
Iterable
│
Collection ──────────────┐
│ │ │
List Set Queue
│ │ │
┌─────────┤ ┌───┴────┐ ┌───┴──────┐
ArrayList LinkedList │ TreeSet ArrayDeque PriorityQueue
HashSet
│
LinkedHashSet
Map (separate hierarchy — not a Collection)
├── HashMap ── LinkedHashMap
└── TreeMap
Choosing the Right Structure
| Need | Use | Key operations & cost |
|---|---|---|
| Indexed, resizable list | ArrayList |
get/set O(1), add amortized O(1), remove(i) O(n) |
| Frequent insert/delete at ends | ArrayDeque |
addFirst/addLast/pollFirst/pollLast all O(1) |
| Key → value lookup | HashMap |
get/put O(1) average |
| Uniqueness check | HashSet |
add/contains O(1) average |
| Sorted keys, range queries | TreeMap |
get/put O(log n), plus floorKey/ceilingKey |
| Insertion-order iteration | LinkedHashMap |
O(1) ops, predictable order — also the basis of an LRU cache |
| Repeatedly get min/max | PriorityQueue |
offer/poll O(log n), peek O(1) |
| Stack | ArrayDeque |
push/pop/peek O(1) |
| Queue | ArrayDeque |
offer/poll O(1) |
⚠️ Use ArrayDeque, not Stack or LinkedList. Stack is a legacy synchronized class; LinkedList has terrible cache locality. ArrayDeque is faster than both and does both jobs. Interviewers notice.
HashMap Idioms Worth Memorizing
These four methods eliminate most boilerplate:
Map<Character, Integer> freq = new HashMap<>();
// Counting
freq.put(c, freq.getOrDefault(c, 0) + 1);
freq.merge(c, 1, Integer::sum); // equivalent, more elegant
// Grouping into lists
Map<String, List<String>> groups = new HashMap<>();
groups.computeIfAbsent(key, k -> new ArrayList<>()).add(value);
// Only set if absent
map.putIfAbsent(key, defaultValue);
computeIfAbsent in particular replaces the four-line "check if key exists, create list, put it back, then add" pattern that clutters so much interview code.
Iteration Patterns
for (Map.Entry<String, Integer> e : map.entrySet()) {
String k = e.getKey();
Integer v = e.getValue();
}
for (String k : map.keySet()) { /* ... */ }
for (Integer v : map.values()) { /* ... */ }
map.forEach((k, v) -> System.out.println(k + " -> " + v));
⚠️ Never modify a collection while iterating it with a for-each loop — you'll get a ConcurrentModificationException. Use an explicit Iterator with it.remove(), or removeIf(...).
Sorting with Comparators
int[][] intervals = /* ... */;
Arrays.sort(intervals, (a, b) -> a[0] - b[0]); // ⚠️ overflow risk!
Arrays.sort(intervals, Comparator.comparingInt(a -> a[0])); // ✅ safe
// Sort objects
people.sort(Comparator.comparing(Person::getLastName)
.thenComparing(Person::getFirstName));
// Descending
nums.sort(Comparator.reverseOrder());
people.sort(Comparator.comparingInt(Person::getAge).reversed());
Subtraction-based comparators are a real bug. If a[0] is -2_000_000_000 and b[0] is 2_000_000_000, a[0] - b[0] overflows and returns the wrong sign. Prefer Integer.compare(a, b) or Comparator.comparingInt.
PriorityQueue
PriorityQueue<Integer> minHeap = new PriorityQueue<>(); // default: min
PriorityQueue<Integer> maxHeap = new PriorityQueue<>(Comparator.reverseOrder());
// Heap of pairs, ordered by second element
PriorityQueue<int[]> pq = new PriorityQueue<>((a, b) -> Integer.compare(a[1], b[1]));
pq.offer(new int[]{1, 5});
int[] smallest = pq.peek(); // does not remove
int[] removed = pq.poll(); // removes and returns
Remember: a PriorityQueue is a heap, not a sorted list. Iterating it does not give sorted order — only repeated poll() does.
Streams — Use With Judgment
List<String> names = people.stream()
.filter(p -> p.getAge() >= 18)
.map(Person::getName)
.sorted()
.collect(Collectors.toList()); // or .toList() in Java 16+
int sum = IntStream.of(nums).sum();
int max = Arrays.stream(nums).max().orElse(Integer.MIN_VALUE);
Map<String, List<Person>> byCity = people.stream()
.collect(Collectors.groupingBy(Person::getCity));
Interview guidance: streams are excellent for expressing data transformations clearly and they signal modern Java competence. But for tight algorithmic loops, write the explicit loop — it's easier to reason about complexity out loud, easier to debug on a whiteboard, and often faster. Use streams where they clarify, not to show off.
Phase 4 — Complexity Analysis (3–4 days)
You cannot skip this. Every interviewer asks "what's the time complexity?" and a wrong answer undoes a correct solution.
The Hierarchy
O(1) < O(log n) < O(n) < O(n log n) < O(n²) < O(n³) < O(2ⁿ) < O(n!)
Reading Constraints to Infer the Expected Solution
This is a genuinely useful interview skill. Assume roughly 10⁸ simple operations per second:
| Input size (n) | Acceptable complexity | Likely technique |
|---|---|---|
| n ≤ 12 | O(n!) | Permutations, brute-force backtracking |
| n ≤ 25 | O(2ⁿ) | Subsets, bitmask enumeration |
| n ≤ 500 | O(n³) | Floyd–Warshall, interval DP |
| n ≤ 5,000 | O(n²) | Nested loops, 2D DP |
| n ≤ 10⁶ | O(n log n) | Sorting, heaps, divide-and-conquer |
| n ≤ 10⁸ | O(n) | Single pass, two pointers, hashing |
| n > 10⁹ | O(log n) or O(1) | Binary search, math formula |
If a problem says 1 <= n <= 10^5, an O(n²) solution will time out. That constraint is a hint about the intended approach. Read constraints first, before writing code. It tells you what you're aiming for and saves you from implementing the wrong algorithm.
Also Master
- Space complexity, including recursion stack depth (a recursive tree traversal is O(h) space, not O(1))
- Amortized analysis — why
ArrayList.addis O(1) amortized despite occasional O(n) resizes - Best/average/worst — quicksort is O(n log n) average, O(n²) worst
- Dropping constants and lower-order terms — O(3n² + 5n + 100) is O(n²)
- The
n log nsorting lower bound for comparison sorts
Phase 5 — Data Structures & Algorithms (6–8 weeks)
The core of the roadmap. Work through topics in this order — each builds on the previous.
5.1 Arrays & Two Pointers (Week 1)
Techniques: opposite-ends two pointers, same-direction (fast/slow) pointers, in-place partitioning, prefix sums, Dutch national flag, cyclic sort, Kadane's algorithm.
// Canonical two-pointer template — sorted array, find a pair summing to target
int left = 0, right = arr.length - 1;
while (left < right) {
int sum = arr[left] + arr[right];
if (sum == target) return new int[]{left, right};
else if (sum < target) left++;
else right--;
}
Representative problems: Two Sum II, Container With Most Water, Trapping Rain Water, 3Sum, Move Zeroes, Sort Colors, Remove Duplicates from Sorted Array, Product of Array Except Self, Maximum Subarray, Merge Sorted Array.
5.2 Sliding Window (Week 1–2)
Fixed-size and variable-size windows. The variable-size template is one of the highest-yield patterns in interviews:
int left = 0, best = 0;
Map<Character, Integer> window = new HashMap<>();
for (int right = 0; right < s.length(); right++) {
char c = s.charAt(right);
window.merge(c, 1, Integer::sum); // expand
while (/* window is invalid */) { // contract
char d = s.charAt(left);
window.merge(d, -1, Integer::sum);
if (window.get(d) == 0) window.remove(d);
left++;
}
best = Math.max(best, right - left + 1); // record
}
Problems: Longest Substring Without Repeating Characters, Minimum Window Substring, Longest Repeating Character Replacement, Permutation in String, Maximum Average Subarray, Sliding Window Maximum (needs a deque), Fruit Into Baskets.
5.3 Hashing (Week 2)
HashMap/HashSet for frequency counting, seen-before checks, grouping, and prefix-sum lookups. Problems: Two Sum, Group Anagrams, Valid Anagram, Longest Consecutive Sequence, Top K Frequent Elements, Subarray Sum Equals K, LRU Cache (with LinkedHashMap or a manual doubly-linked list + map), Contains Duplicate.
5.4 Binary Search (Week 2–3)
Not just "find element in sorted array." The real skill is binary searching on the answer space.
// Overflow-safe template
int lo = 0, hi = arr.length - 1;
while (lo <= hi) {
int mid = lo + (hi - lo) / 2; // NOT (lo + hi) / 2
if (arr[mid] == target) return mid;
else if (arr[mid] < target) lo = mid + 1;
else hi = mid - 1;
}
return -1; // or lo, for insertion position
(lo + hi) / 2 overflows when both are large. Always write lo + (hi - lo) / 2.
Problems: Binary Search, Search in Rotated Sorted Array, Find Minimum in Rotated Sorted Array, Find First and Last Position, Koko Eating Bananas, Capacity to Ship Packages, Median of Two Sorted Arrays, Split Array Largest Sum, Search a 2D Matrix.
5.5 Stacks & Queues (Week 3)
Monotonic stacks, expression parsing, the next-greater-element family, and queue-based simulation.
Problems: Valid Parentheses, Min Stack, Daily Temperatures, Next Greater Element, Largest Rectangle in Histogram, Evaluate Reverse Polish Notation, Implement Queue using Stacks, Decode String, Asteroid Collision.
5.6 Linked Lists (Week 3)
class ListNode {
int val;
ListNode next;
ListNode(int val) { this.val = val; }
}
Techniques: dummy head nodes (they eliminate almost all edge cases), fast/slow pointers for cycle detection and finding the middle, iterative reversal, merging.
// Iterative reversal — know this cold
ListNode prev = null, curr = head;
while (curr != null) {
ListNode next = curr.next;
curr.next = prev;
prev = curr;
curr = next;
}
return prev;
Problems: Reverse Linked List, Merge Two Sorted Lists, Linked List Cycle, Remove Nth Node From End, Reorder List, Add Two Numbers, Copy List with Random Pointer, Merge k Sorted Lists, LRU Cache.
5.7 Recursion & Backtracking (Week 4)
Recursion is the gateway to trees, graphs, and DP. Do not rush it.
Every recursive function needs three things: a base case, a recursive call on a smaller subproblem, and combination of results.
// Universal backtracking template
void backtrack(List<List<Integer>> result, List<Integer> path, int[] nums, int start) {
result.add(new ArrayList<>(path)); // ⚠️ copy, don't add the mutable reference
for (int i = start; i < nums.length; i++) {
path.add(nums[i]); // choose
backtrack(result, path, nums, i + 1); // explore
path.remove(path.size() - 1); // un-choose
}
}
The new ArrayList<>(path) copy is the classic bug: add path directly and every entry in your result points at the same list, which ends up empty.
Problems: Subsets, Permutations, Combination Sum, Palindrome Partitioning, N-Queens, Word Search, Letter Combinations of a Phone Number, Generate Parentheses, Sudoku Solver.
5.8 Trees (Week 5)
Cover binary trees, BSTs, traversals (preorder, inorder, postorder — both recursive and iterative), level-order BFS, depth/height, path problems, and tree construction.
class TreeNode {
int val;
TreeNode left, right;
TreeNode(int val) { this.val = val; }
}
// Level-order traversal — the BFS template you'll reuse constantly
List<List<Integer>> levelOrder(TreeNode root) {
List<List<Integer>> result = new ArrayList<>();
if (root == null) return result;
Queue<TreeNode> queue = new ArrayDeque<>();
queue.offer(root);
while (!queue.isEmpty()) {
int size = queue.size(); // snapshot: this level's node count
List<Integer> level = new ArrayList<>();
for (int i = 0; i < size; i++) {
TreeNode node = queue.poll();
level.add(node.val);
if (node.left != null) queue.offer(node.left);
if (node.right != null) queue.offer(node.right);
}
result.add(level);
}
return result;
}
Key BST insight: an inorder traversal of a BST yields sorted order. A surprising number of BST problems reduce to that one fact.
Problems: Maximum Depth, Invert Binary Tree, Same Tree, Symmetric Tree, Validate BST, Lowest Common Ancestor (both BST and general), Binary Tree Level Order Traversal, Path Sum, Diameter of Binary Tree, Serialize and Deserialize Binary Tree, Kth Smallest in BST, Construct Tree from Preorder and Inorder.
5.9 Heaps & Top-K (Week 5–6)
Techniques: min-heap of size k for "k largest," max-heap of size k for "k smallest," two heaps for a running median, heap-based k-way merge.
Problems: Kth Largest Element in an Array, Top K Frequent Elements, Merge k Sorted Lists, Find Median from Data Stream, K Closest Points to Origin, Task Scheduler, Reorganize String.
5.10 Graphs (Week 6)
Representations (adjacency list is almost always right), BFS, DFS, cycle detection, topological sort, union-find, and shortest paths.
// Adjacency list
List<List<Integer>> graph = new ArrayList<>();
for (int i = 0; i < n; i++) graph.add(new ArrayList<>());
graph.get(u).add(v);
// BFS — shortest path in an unweighted graph
boolean[] visited = new boolean[n];
Queue<Integer> queue = new ArrayDeque<>();
queue.offer(start);
visited[start] = true;
int distance = 0;
while (!queue.isEmpty()) {
int size = queue.size();
for (int i = 0; i < size; i++) {
int node = queue.poll();
for (int neighbor : graph.get(node)) {
if (!visited[neighbor]) {
visited[neighbor] = true;
queue.offer(neighbor);
}
}
}
distance++;
}
Also learn Union-Find with path compression — it's short and unlocks a whole problem class:
class DSU {
int[] parent, rank;
DSU(int n) {
parent = new int[n];
rank = new int[n];
for (int i = 0; i < n; i++) parent[i] = i;
}
int find(int x) {
if (parent[x] != x) parent[x] = find(parent[x]); // path compression
return parent[x];
}
boolean union(int a, int b) {
int ra = find(a), rb = find(b);
if (ra == rb) return false; // already connected
if (rank[ra] < rank[rb]) { int t = ra; ra = rb; rb = t; }
parent[rb] = ra;
if (rank[ra] == rank[rb]) rank[ra]++;
return true;
}
}
Problems: Number of Islands, Clone Graph, Course Schedule I & II, Pacific Atlantic Water Flow, Rotting Oranges, Word Ladder, Number of Connected Components, Redundant Connection, Network Delay Time (Dijkstra), Alien Dictionary.
5.11 Dynamic Programming (Week 7)
The topic that intimidates everyone. It becomes manageable once you follow a fixed procedure:
- Define the state. What does
dp[i]mean? Write it in a comment, in words. - Find the recurrence. How does
dp[i]relate to earlier states? - Set base cases.
- Choose direction. Top-down memoization or bottom-up tabulation.
- Optimize space if only the last one or two rows are needed.
// Climbing Stairs — the "hello world" of DP
// State: dp[i] = number of distinct ways to reach step i
int climbStairs(int n) {
if (n <= 2) return n;
int prev2 = 1, prev1 = 2;
for (int i = 3; i <= n; i++) {
int curr = prev1 + prev2;
prev2 = prev1;
prev1 = curr;
}
return prev1; // O(n) time, O(1) space
}
Learn these DP families in order — 1D linear, then 2D grid, then knapsack, then subsequence, then interval:
| Family | Representative problems |
|---|---|
| 1D linear | Climbing Stairs, House Robber, Fibonacci, Decode Ways |
| Grid | Unique Paths, Minimum Path Sum, Maximal Square |
| Knapsack | Partition Equal Subset Sum, Coin Change, Target Sum |
| Subsequence | Longest Increasing Subsequence, Longest Common Subsequence, Edit Distance |
| String matching | Word Break, Regular Expression Matching, Palindromic Substrings |
| Interval | Burst Balloons, Matrix Chain Multiplication |
| Stock/state machine | Best Time to Buy and Sell Stock I–IV, with cooldown, with fee |
Learning tactic that works: solve every problem twice. First with recursion + memoization (easier to derive from the problem statement), then convert to a bottom-up table. Doing that conversion a dozen times is how DP finally clicks.
5.12 Greedy & Intervals (Week 8)
The greedy skill is proving the greedy choice is safe — be ready to argue why locally optimal implies globally optimal.
Problems: Merge Intervals, Insert Interval, Non-overlapping Intervals, Meeting Rooms I & II, Jump Game I & II, Gas Station, Partition Labels, Minimum Number of Arrows.
5.13 Bit Manipulation & Math (Week 8)
x & 1 // is odd
x >> 1 // divide by 2
x << 1 // multiply by 2
x & (x - 1) // clear the lowest set bit
x & (-x) // isolate the lowest set bit
x ^ x == 0 // XOR self-cancels — the trick behind "Single Number"
Integer.bitCount(x) // count set bits
(x & (x - 1)) == 0 // is a power of two (for x > 0)
1 << i // i-th bit mask
x >>> 1 // unsigned right shift — Java-specific, fills with 0
Note >>> — Java's unsigned right shift, which doesn't preserve the sign bit. It's the reason (lo + hi) >>> 1 is a valid overflow-safe midpoint too.
Problems: Single Number I & II, Number of 1 Bits, Counting Bits, Reverse Bits, Missing Number, Sum of Two Integers, Subsets via bitmask, GCD/LCM, Sieve of Eratosthenes, Pow(x, n).
5.14 Tries & Advanced (Optional, Week 8+)
Tries for prefix problems, segment trees and Fenwick trees for range queries, Floyd–Warshall, Kruskal/Prim for MST. Only pursue these if you're targeting companies with a reputation for hard rounds.
Phase 6 — Pattern Recognition (Ongoing)
At around 150 solved problems, shift your goal from "solve this problem" to "recognize which of ~20 patterns this is." That shift is what makes you fast.
| If the problem says… | Reach for… |
|---|---|
| "sorted array" + find pair/triplet | Two pointers or binary search |
| "contiguous subarray/substring" | Sliding window or prefix sums |
| "k largest / k smallest / k most frequent" | Heap of size k |
| "all permutations / combinations / subsets" | Backtracking |
| "shortest path, unweighted" | BFS |
| "connected components / islands" | DFS, BFS, or union-find |
| "count the ways" or "min/max cost" | Dynamic programming |
| "next greater / previous smaller" | Monotonic stack |
| "detect a cycle" | Fast/slow pointers (list) or DFS colors (graph) |
| "minimize the maximum" / "maximize the minimum" | Binary search on the answer |
| "prerequisites / build order / dependencies" | Topological sort |
| "prefix / autocomplete / dictionary" | Trie |
| "overlapping ranges" | Sort by start, then sweep |
| "top-k in a stream" or "running median" | Heap, or two heaps |
| "in-place, O(1) extra space" | Two pointers or index-as-hash-key |
| "n ≤ 20" | Bitmask enumeration or exponential backtracking |
Build this table yourself as you practice. A table you wrote from your own solves is worth ten you copied.
Phase 7 — Java-Specific Interview Questions (1 week)
Java interviews aren't only algorithms. Backend rounds probe language internals. Be ready for:
Memory & JVM
- Stack vs heap: what lives where, and why
- How garbage collection works conceptually; generational GC; what "stop the world" means
- Memory leaks in a GC language (static collections that grow forever, unclosed resources, listeners never deregistered)
Stringinterning and the string pool; whynew String("a") != "a"
Language mechanics
==vsequals()- Pass-by-value semantics — Java always passes by value, but for objects the value is the reference. Say it precisely.
- Autoboxing, unboxing, and the
Integercache (-128to127) - Generics and type erasure; why you can't write
new T[]; wildcards? extendsvs? super(PECS: Producer Extends, Consumer Super) final,finally,finalize()— three unrelated things with confusingly similar names- Shallow vs deep copy
- Immutability: how to design a truly immutable class
Concurrency (mid-level and up)
ThreadvsRunnablevsCallablesynchronized,volatile, and what each actually guaranteesExecutorServiceand thread pools- Deadlock: the four conditions, and how to avoid it
ConcurrentHashMapvsCollections.synchronizedMapAtomicIntegerand compare-and-swap- Virtual threads (Java 21+) — worth mentioning if you know them
Design
- SOLID principles, with a concrete example of each
- Design patterns you'll actually be asked to code: Singleton (and why the enum version is best), Factory, Builder, Observer, Strategy, Decorator
- Low-level design exercises: parking lot, elevator, deck of cards, vending machine, rate limiter, Snake game. Practice these — they're common at mid-level and above, and they reward the OOP work from Phase 2.
Phase 8 — Mock Interviews & Behavioral Prep (2 weeks, overlapping)
Solving problems alone does not prepare you for solving them out loud, while being watched, with someone interrupting you. Those are different skills.
Do at Least 10 Timed Mocks
Use Pramp, interviewing.io, or a friend. Rules: 45 minutes, no IDE autocomplete, talk continuously.
The Interview Protocol — Follow It Every Time
- Restate the problem. "So I'm given an array of integers and a target, and I need to return the indices of two numbers that sum to it — is that right?" Catches misunderstandings in 20 seconds.
- Ask about constraints. Input size? Negative numbers? Duplicates? Empty input? Is the array sorted? Can I modify it? Interviewers deliberately leave these out to see if you ask.
- Work a small example by hand. On paper or in a comment.
- State a brute-force approach and its complexity. Never stay silent while thinking. "The naive approach is nested loops, O(n²) — let me see if I can do better."
- Propose an optimization and get buy-in. "I could use a hash map to get this to O(n) time with O(n) space. Should I code that?"
- Code it, narrating as you go. Meaningful variable names. Don't write
a,b,temp. - Trace through your own code with the example. Out loud. Find your own bugs before the interviewer does — this is a strong signal.
- Discuss edge cases and complexity. Null, empty, single element, all duplicates, overflow.
- Mention improvements you didn't implement. Shows depth beyond the immediate solution.
Fix These Habits
- Silent thinking for more than 15 seconds → narrate instead
- Coding immediately without clarifying → always clarify first
- Ignoring a hint → interviewers give hints when you're off-track; take them
- Getting defensive about a bug → "Good catch, let me fix that" and move on
- Arguing that your approach is fine when told to optimize
Behavioral Prep
Prepare 6–8 stories in STAR format (Situation, Task, Action, Result) covering: a hard technical problem, a conflict with a teammate, a failure and what you learned, leading without authority, a tight deadline, receiving hard feedback, and a time you changed your mind. Have specific numbers where you can. These stories get reused across every behavioral question you'll be asked.
Timeline Summary
14-Week Plan (knows another language, ~12 hrs/week)
| Weeks | Focus |
|---|---|
| 1 | Setup + core Java syntax |
| 2–3 | OOP + equals/hashCode + records |
| 4 | Collections + streams + complexity analysis |
| 5 | Arrays, two pointers, sliding window, hashing |
| 6 | Binary search, stacks, queues, linked lists |
| 7 | Recursion + backtracking |
| 8 | Trees + BSTs |
| 9 | Heaps + graphs (BFS/DFS) |
| 10 | Graphs advanced + union-find + topological sort |
| 11–12 | Dynamic programming |
| 13 | Greedy, intervals, bit manipulation, Java internals |
| 14 | Mocks, LLD, behavioral, review weak areas |
8-Week Compressed Plan (already knows Java)
| Weeks | Focus |
|---|---|
| 1 | Collections/streams refresher + complexity + arrays/two pointers/sliding window |
| 2 | Hashing + binary search + stacks/queues |
| 3 | Linked lists + recursion/backtracking |
| 4 | Trees + heaps |
| 5 | Graphs (all of it) |
| 6 | Dynamic programming |
| 7 | Greedy, intervals, bits, Java internals, LLD |
| 8 | Mocks + behavioral + targeted review |
Part 4 — The Java Interview Cheat Sheet
Keep this in front of you until it's memorized.
Conversions
int i = Integer.parseInt("42");
String s = String.valueOf(42); // or Integer.toString(42)
char c = '7';
int digit = c - '0'; // 7 — character arithmetic
char letter = (char) ('a' + 3); // 'd'
int index = c - 'a'; // 0-25 for lowercase letters
char[] chars = str.toCharArray();
String back = new String(chars);
String joined = String.join(",", list);
String[] parts = str.split("\\s+"); // split on whitespace (regex!)
Array ↔ List
// int[] → List<Integer>
List<Integer> list = Arrays.stream(arr).boxed().collect(Collectors.toList());
// List<Integer> → int[]
int[] arr = list.stream().mapToInt(Integer::intValue).toArray();
// Object array → List (fixed-size view — cannot add/remove!)
List<String> view = Arrays.asList(strArray);
List<String> mutable = new ArrayList<>(Arrays.asList(strArray));
// Immutable list literal (Java 9+)
List<Integer> fixed = List.of(1, 2, 3);
⚠️ Arrays.asList returns a fixed-size view backed by the array. Calling add on it throws UnsupportedOperationException. Wrap it in new ArrayList<>(...) when you need mutation.
Useful Constants & Math
Integer.MAX_VALUE, Integer.MIN_VALUE
Long.MAX_VALUE, Long.MIN_VALUE
Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY
Math.max(a, b); Math.min(a, b); Math.abs(x);
Math.pow(2, 10); // returns double!
Math.sqrt(x); Math.floor(x); Math.ceil(x);
Math.floorDiv(-7, 2); // -4 (true floor, unlike / which gives -3)
Math.floorMod(-7, 3); // 2 (always non-negative — very useful)
Math.floorMod deserves attention: Java's % returns a negative result for negative operands, which breaks circular-array indexing. Math.floorMod(-1, 5) returns 4, which is what you almost always want.
Fast Input (For Competitive Programming)
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
int n = Integer.parseInt(br.readLine().trim());
StringTokenizer st = new StringTokenizer(br.readLine());
int a = Integer.parseInt(st.nextToken());
StringBuilder out = new StringBuilder();
out.append(answer).append('\n');
System.out.print(out);
Scanner is roughly 5–10× slower than BufferedReader. Fine for interviews; a TLE risk in contests.
TreeMap — The Sorted-Map Superpower
TreeMap<Integer, String> tm = new TreeMap<>();
tm.firstKey(); tm.lastKey();
tm.floorKey(k); // greatest key ≤ k
tm.ceilingKey(k); // smallest key ≥ k
tm.lowerKey(k); // greatest key < k
tm.higherKey(k); // smallest key > k
tm.headMap(k); tm.tailMap(k); tm.subMap(a, b);
floorKey/ceilingKey turn many otherwise-hard problems (calendar booking, range assignment, nearest-value queries) into a few lines.
Part 5 — 15 Java Gotchas That Fail Interviews
1. == vs equals() on objects. == compares references; equals() compares content. String a = new String("hi"); a == "hi" is false. Always use equals() for objects.
2. The Integer cache. Java caches boxed integers from −128 to 127. So Integer a = 127, b = 127; a == b is true, but Integer a = 128, b = 128; a == b is false. Never compare boxed integers with ==.
3. String concatenation in loops. O(n²). Use StringBuilder.
4. Integer overflow in the binary-search midpoint. Use lo + (hi - lo) / 2.
5. Subtraction comparators. (a, b) -> a - b overflows. Use Integer.compare(a, b).
6. arr.length vs list.size() vs str.length(). A field, a method, and a method. Mixing them up is an instant compile error and looks careless.
7. Integer division truncation. 5 / 2 == 2. For a decimal result, cast: (double) 5 / 2.
8. Arrays.asList is immutable in size. Wrap in new ArrayList<>() to modify.
9. Modifying a collection during for-each iteration. Throws ConcurrentModificationException. Use Iterator.remove() or removeIf().
10. Shallow copy of 2D arrays. Arrays.copyOf(grid, n) copies row references, not rows. Clone each row individually.
11. Forgetting to copy in backtracking. result.add(path) stores a reference to a list you're about to mutate. Use result.add(new ArrayList<>(path)).
12. Not overriding hashCode with equals. Your objects will not be findable in a HashSet or HashMap.
13. char + char promotes to int. 'a' + 'b' is 195, not a string. Cast back with (char) or build with a StringBuilder.
14. PriorityQueue iteration isn't sorted. Only poll() gives ordered output. Printing a PriorityQueue directly shows heap order, which surprises people mid-interview.
15. Negative modulo. -7 % 3 == -1 in Java (unlike Python's 2). Use Math.floorMod for circular indexing.
Part 6 — Worked Examples
Three problems showing the pattern-to-code pipeline.
Example 1 — Two Sum (Hashing)
Problem: Given nums and target, return indices of the two numbers summing to target.
Reasoning: Brute force is nested loops, O(n²). The insight: for each element x, I need to know whether target - x has already been seen. "Have I seen it, and where?" is exactly a HashMap<value, index>. One pass, checking before inserting, so I never pair an element with itself.
public int[] twoSum(int[] nums, int target) {
Map<Integer, Integer> seen = new HashMap<>(); // value → index
for (int i = 0; i < nums.length; i++) {
int complement = target - nums[i];
if (seen.containsKey(complement)) {
return new int[]{seen.get(complement), i};
}
seen.put(nums[i], i); // insert after checking
}
return new int[]{-1, -1}; // no solution
}
Complexity: O(n) time, O(n) space. Trading space for time — a theme you'll repeat all interview season.
Example 2 — Longest Substring Without Repeating Characters (Sliding Window)
Reasoning: "Longest contiguous substring satisfying a property" is the sliding-window signature. Expand right always; when a duplicate appears, shrink from left until the window is valid again. Each character enters and leaves at most once, so it's O(n) despite the nested loop.
public int lengthOfLongestSubstring(String s) {
Map<Character, Integer> lastSeen = new HashMap<>();
int longest = 0, left = 0;
for (int right = 0; right < s.length(); right++) {
char c = s.charAt(right);
// If we've seen c inside the current window, jump left past it
if (lastSeen.containsKey(c) && lastSeen.get(c) >= left) {
left = lastSeen.get(c) + 1;
}
lastSeen.put(c, right);
longest = Math.max(longest, right - left + 1);
}
return longest;
}
Complexity: O(n) time, O(min(n, charset)) space.
Note the >= left check. Without it, a character last seen before the window starts would incorrectly move left backwards. That's the bug an interviewer will probe for.
Example 3 — Validate BST (Trees + Recursion)
Reasoning: The tempting wrong answer is checking only left.val < node.val < right.val locally. That fails because a node deep in the left subtree must be less than every ancestor on its right-going path, not just its parent. The fix is to pass down a valid (min, max) range and narrow it at each step. Long bounds handle nodes at Integer.MIN_VALUE/MAX_VALUE.
public boolean isValidBST(TreeNode root) {
return validate(root, Long.MIN_VALUE, Long.MAX_VALUE);
}
private boolean validate(TreeNode node, long min, long max) {
if (node == null) return true; // empty tree is valid
if (node.val <= min || node.val >= max) return false;
return validate(node.left, min, node.val) // upper bound tightens
&& validate(node.right, node.val, max); // lower bound tightens
}
Complexity: O(n) time, O(h) space for the recursion stack — O(log n) balanced, O(n) worst case.
Discuss the alternative: an inorder traversal must produce strictly increasing values. Mentioning both approaches, and why you chose one, is exactly the depth interviewers reward.
Part 7 — Your Practice System
Volume Targets
| Total solved | What you're ready for |
|---|---|
| 50 | Basic screens; you know the vocabulary |
| 100 | Most mid-level interviews at non-FAANG companies |
| 150–200 | Solid FAANG-level preparation |
| 300+ | Diminishing returns — you're now polishing, not learning |
Difficulty mix: roughly 25% easy, 60% medium, 15% hard. Mediums are where interviews actually live. Chasing hards early builds frustration, not skill.
The Daily Session (90 minutes)
- 10 min — review yesterday's problems from memory. Can you still state the approach? This is spaced repetition, and it's the step most people skip.
- 60 min — 2 new problems. Set a 25-minute timer per problem. When it rings, look at the solution, understand it fully, then close it and reimplement from scratch.
- 20 min — write a note for each: the pattern name, the key insight, the complexity, and the bug you hit.
That last 20 minutes is the highest-value part of the session. Reviewing 100 problems well beats solving 300 and forgetting them.
The 25-Minute Rule
Struggling productively builds skill; struggling unproductively builds despair. Twenty-five minutes of genuine effort, then read the editorial. But never mark a problem done because you read the solution — you're done when you can write it from an empty file.
The Retry Queue
Keep a list of every problem you couldn't solve unaided. Redo them after 3 days, then 1 week, then 3 weeks. A problem you solved once and forgot has taught you nothing.
Track Your Weak Spots
Log every failure with a cause: "misread the constraint," "didn't recognize it as DP," "off-by-one in the window," "forgot to handle empty input." After 50 problems, patterns emerge and you'll know precisely what to drill. This log is more valuable than your solved count.
Resources
Problems: LeetCode (primary — Blind 75 for a fast pass, NeetCode 150 for a fuller one), HackerRank, Codeforces if you enjoy contests.
Books: Effective Java by Joshua Bloch — the definitive book on writing good Java, and the source of many "why" answers interviewers want. Cracking the Coding Interview for interview mechanics. Grokking Algorithms if you need friendly, visual intuition first. Java: The Complete Reference (Herbert Schildt) as a reference.
Practice tools: Pramp and interviewing.io for free peer mocks. Excalidraw or plain paper for diagramming before you code.
Part 8 — Interview Day Playbook
The night before: don't cram. Reread your own pattern notes for 30 minutes, then stop. Sleep matters more than one more medium.
Before the call: test your camera, mic, and internet. Have water and paper. Close everything else. Have your questions for them written down.
In the first minute: be warm and clear. Rapport is real — interviewers advocate for people they enjoyed talking to.
When you're stuck: say so, productively. "I'm considering two directions — a hash map for O(n) space, or sorting first for O(1) extra space. Let me think about which fits the constraints better." That's not weakness; it's exactly the collaborative thinking they're evaluating. Silence is the only real failure mode.
When you're wrong: "You're right, that breaks on duplicates. Let me handle that." Move on immediately. How you take correction is itself a data point, often a heavily weighted one.
When you finish early: don't sit quietly. Discuss the alternative approach, the trade-offs, how you'd test it, or how it would change if the input didn't fit in memory.
Your questions for them: ask something real. What does the first month look like? How does the team handle technical disagreement? What's the biggest challenge the team faces this quarter? Generic questions read as disinterest.
Afterward: write down every question you were asked while it's fresh. Whatever the outcome, that record makes your next interview better.
Final Thoughts
Two things separate people who succeed from people who grind for a year and stall:
Consistency beats intensity. Ninety focused minutes a day for three months will take you further than twelve-hour weekend marathons. The material needs repeated exposure over time, not compression.
Understanding beats memorization. Two hundred memorized solutions collapse the moment an interviewer changes one constraint. Twenty deeply understood patterns adapt to anything. When you finish a problem, don't ask "did I get it right?" — ask "what class of problems does this technique solve, and how would I recognize the next one?"
Java's verbosity will feel like a handicap in week one. By week ten the idioms are automatic, and the type system starts working for you: your data model is visible in your declarations, your mistakes surface at compile time, and the standard library has already built every data structure you need.
Start with Phase 0 today. Solve one problem. Commit it. Then do it again tomorrow.