Java Coding Interview Cheat Sheet — Complete Collections & Syntax Guide
A practical, no-fluff guide to the Java syntax, Collections framework, and idioms you’ll reach for most during a technical interview.
Why Java for Coding Interviews?
Java is verbose, but it remains one of the most widely used languages in interviews at top companies like Amazon and Google. Here is why it’s a solid choice:
- Strong Type System: In a whiteboard or raw text editor setting where you can’t run code, Java’s strict typing prevents entire classes of silly errors (like passing a string to an integer parameter) that would cause instant crashes in Python.
- The Collections Framework:
HashMap,PriorityQueue,TreeMap— Java’s standard library is incredibly comprehensive, consistent, and provides all the data structures required for any interview problem. - Ubiquity: Every interviewer knows Java. No matter who you are paired with, they will be able to read and understand your code quickly.
If you're building your full preparation plan, start with our How to Prepare for a Coding Interview guide. This also pairs perfectly with Data Structures for Coding Interviews.
Looking for a different language? Python, C++, JavaScript, Go.
Already know why Java? Try a free Java mock interview on Intervu →
1. Data Types & Variables
// Primitive types
int x = 10;
long big = (long) 1e18; // use long for large numbers!
double pi = 3.14159;
float f = 3.14f;
boolean flag = true; // true / false (lowercase)
char c = 'A';
// Autoboxing — primitives ↔ wrapper types
Integer boxed = 42; // int → Integer (auto)
int unboxed = boxed; // Integer → int (auto)
// Important wrapper methods
Integer.MAX_VALUE // 2^31 - 1 ≈ 2.1 × 10^9
Integer.MIN_VALUE // -2^31
Long.MAX_VALUE // 2^63 - 1 ≈ 9.2 × 10^18
Long.MIN_VALUE
// Infinity proxies
int INF = Integer.MAX_VALUE;
long LINF = Long.MAX_VALUE;
// Multiple assignment
int a = 1, b = 2, c = 3;
// Swap (no built-in swap for primitives)
int tmp = a; a = b; b = tmp;
// var (Java 10+) — type inference
var list = new ArrayList<Integer>();
var map = new HashMap<String, Integer>();
// Constants
final int MOD = 1_000_000_007; // underscores for readability
Key tip: Java int overflows silently at ~2.1×10⁹. Always cast to long before multiplying: (long) a * b. Integer division truncates — use (double) a / b when you need the decimal. Watch out: Math.abs(Integer.MIN_VALUE) == Integer.MIN_VALUE (stays negative due to overflow!) — a nasty bug when negating values.
2. Strings
Strings are immutable in Java and appear in nearly every coding interview — from anagram detection to sliding window problems.
String s = "hello world";
// Basics
s.length() // 11
s.charAt(0) // 'h'
s.charAt(s.length() - 1) // 'd'
s.substring(6) // "world"
s.substring(1, 5) // "ello" [start, end)
// Search
s.indexOf("world") // 6 (-1 if not found)
s.indexOf('o') // 4 first occurrence
s.lastIndexOf('o') // 7 last occurrence
s.contains("world") // true
s.startsWith("he") // true
s.endsWith("ld") // true
// Transform
s.toUpperCase() // "HELLO WORLD"
s.toLowerCase() // "hello world"
s.trim() // remove leading/trailing whitespace
s.strip() // unicode-aware trim (Java 11+)
s.replace('l', 'r') // "herro worrd"
s.replace("world", "there") // replace substring
// Split & Join
s.split(" ") // ["hello", "world"]
s.split(",") // split by delimiter
String.join(", ", "a", "b") // "a, b"
String.join("-", list) // join a collection
// Char checks
Character.isLetter(c)
Character.isDigit(c)
Character.isLetterOrDigit(c)
Character.isUpperCase(c)
Character.isLowerCase(c)
Character.toUpperCase(c)
Character.toLowerCase(c)
// Char ↔ int
'a' - 'a' // 0
c - '0' // digit char → int value
(int) 'a' // 97
// String ↔ int
Integer.parseInt("123") // "123" → 123
String.valueOf(42) // 42 → "42"
Integer.toString(42) // same
// String → char array and back
char[] chars = s.toCharArray();
String back = new String(chars);
// Comparison — NEVER use == for strings!
s1.equals(s2) // content equality
s1.equalsIgnoreCase(s2)
s1.compareTo(s2) // lexicographic; 0 if equal
// StringBuilder — use for building strings in a loop
StringBuilder sb = new StringBuilder();
sb.append("hello");
sb.append(' ');
sb.append("world");
sb.insert(5, ","); // insert at index
sb.deleteCharAt(5); // remove char at index
sb.reverse(); // reverse in-place
sb.toString(); // convert to String
// Check if palindrome
new StringBuilder(s).reverse().toString().equals(s)
Key tip: Never compare strings with == in Java — it compares references, not content. Always use .equals(). And never build a string with += inside a loop; use StringBuilder to avoid O(n²) behavior.
3. Arrays
// Declare & initialize
int[] arr = {3, 1, 4, 1, 5};
int[] zeros = new int[10]; // default-initialized to 0
int[] filled = new int[10];
Arrays.fill(filled, -1); // fill with -1
// 2D array
int[][] matrix = new int[rows][cols];
int[][] grid = {{1,2},{3,4}};
// Access
arr[0] // 3
arr[arr.length - 1] // last element
arr.length // 5 (field, not method!)
// Sort
Arrays.sort(arr); // ascending, in-place — O(n log n)
Arrays.sort(arr, 2, 5); // sort subarray [2, 5)
// Sort Integer[] with custom comparator (can't use int[])
Integer[] boxed = {3, 1, 4};
Arrays.sort(boxed, (a, b) -> b - a); // descending
Arrays.sort(boxed, Comparator.reverseOrder()); // same
// Binary search (array must be sorted)
Arrays.binarySearch(arr, 4) // index, or negative if missing
// Copy
int[] copy = Arrays.copyOf(arr, arr.length);
int[] sub = Arrays.copyOfRange(arr, 1, 4); // [1, 4)
// Fill, equals, toString
Arrays.fill(arr, 0);
Arrays.equals(arr1, arr2);
Arrays.toString(arr); // "[3, 1, 4, 1, 5]"
// Convert array ↔ List
List<Integer> list = Arrays.asList(boxed); // fixed-size!
List<Integer> mutable = new ArrayList<>(Arrays.asList(boxed));
Integer[] back = list.toArray(new Integer[0]);
// Streams (Java 8+)
int sum = Arrays.stream(arr).sum();
int max = Arrays.stream(arr).max().getAsInt();
int min = Arrays.stream(arr).min().getAsInt();
Key tip: arr.length is a field, not a method — no parentheses. list.size() for collections IS a method. Mixing these up under pressure is a classic Java interview slip.
4. ArrayList & LinkedList
import java.util.*;
// ArrayList — dynamic array, O(1) access, O(n) insert/delete
List<Integer> list = new ArrayList<>();
list.add(3); // append — O(1) amortized
list.add(0, 99); // insert at index — O(n)
list.get(0) // 99
list.set(0, 5) // update at index
list.remove(Integer.valueOf(3)) // remove by value — O(n)
list.remove(0) // remove by index — O(n)
list.size() // number of elements
list.isEmpty()
list.contains(5) // O(n)
list.indexOf(5) // first index, -1 if missing
// Initialize with values
List<Integer> init = new ArrayList<>(Arrays.asList(1, 2, 3));
List<Integer> fixed = List.of(1, 2, 3); // immutable (Java 9+)
// 2D ArrayList
List<List<Integer>> graph = new ArrayList<>();
for (int i = 0; i < n; i++)
graph.add(new ArrayList<>());
graph.get(0).add(1);
// Iterate
for (int x : list) { }
for (int i = 0; i < list.size(); i++) { }
list.forEach(x -> System.out.println(x));
// Sort
Collections.sort(list);
Collections.sort(list, Collections.reverseOrder());
list.sort((a, b) -> b - a);
// Reverse, shuffle, fill
Collections.reverse(list);
Collections.shuffle(list);
Collections.fill(list, 0);
// Min / Max
Collections.min(list)
Collections.max(list)
// Sublist (view, backed by original)
list.subList(1, 4)
// LinkedList — O(1) at head/tail, O(n) random access
LinkedList<Integer> ll = new LinkedList<>();
ll.addFirst(1); ll.addLast(2);
ll.removeFirst(); ll.removeLast();
ll.peekFirst(); ll.peekLast();
5. Stack & Queue & Deque
import java.util.*;
// Stack — use Deque, not legacy Stack class
Deque<Integer> stack = new ArrayDeque<>();
stack.push(1); // push to front
stack.push(2);
stack.peek() // view top — 2
stack.pop() // remove top — 2
stack.isEmpty()
// Queue (FIFO) — use Deque or LinkedList
Queue<Integer> queue = new LinkedList<>();
queue.offer(1); // enqueue
queue.offer(2);
queue.peek() // view front — 1
queue.poll() // dequeue — 1 (returns null if empty)
queue.isEmpty()
// Deque — double-ended, use for both stack and queue
Deque<Integer> dq = new ArrayDeque<>();
dq.offerFirst(0); // add to front
dq.offerLast(1); // add to back
dq.peekFirst(); // view front
dq.peekLast(); // view back
dq.pollFirst(); // remove front
dq.pollLast(); // remove back
dq.size()
dq.isEmpty()
Key tip: Prefer ArrayDeque over LinkedList for both stack and queue — it’s faster with less memory overhead. Never use the legacy Stack class in interviews; it’s synchronized and slow.
6. HashSet & TreeSet
import java.util.*;
// HashSet — unordered, unique, O(1) average
Set<Integer> set = new HashSet<>();
set.add(1); set.add(2); set.add(2); // {1, 2}
set.remove(1);
set.contains(2) // true — O(1)
set.size()
set.isEmpty()
// Initialize from collection
Set<Integer> fromList = new HashSet<>(list);
// Set operations
set1.retainAll(set2) // intersection (modifies set1)
set1.addAll(set2) // union
set1.removeAll(set2) // difference
// Iterate (unordered)
for (int x : set) { }
// TreeSet — sorted, unique, O(log n)
TreeSet<Integer> ts = new TreeSet<>();
ts.add(3); ts.add(1); ts.add(4); // [1, 3, 4]
ts.first() // 1 (min)
ts.last() // 4 (max)
ts.floor(3) // largest element <= 3 → 3
ts.ceiling(2) // smallest element >= 2 → 3
ts.lower(3) // strictly less → 1
ts.higher(3) // strictly greater → 4
ts.headSet(3) // elements < 3 → [1]
ts.tailSet(3) // elements >= 3 → [3, 4]
// Iterate TreeSet (sorted)
for (int x : ts) { }
for (int x : ts.descendingSet()) { } // reverse order
7. HashMap & TreeMap
HashMap is the single most-used collection in Java coding interviews — for frequency maps, adjacency lists, memoization, and more.
import java.util.*;
// HashMap — O(1) average, unordered
Map<String, Integer> map = new HashMap<>();
map.put("alice", 10);
map.put("bob", 20);
map.get("alice") // 10
map.get("zz") // null
map.getOrDefault("zz", 0) // 0 (safe default)
map.containsKey("alice") // true
map.containsValue(10) // true
map.remove("bob") // removes key, returns value
map.size()
map.isEmpty()
// putIfAbsent / compute
map.putIfAbsent("carol", 0)
map.put("carol", map.getOrDefault("carol", 0) + 1)
// getOrDefault frequency pattern
Map<Integer, Integer> freq = new HashMap<>();
for (int x : arr)
freq.put(x, freq.getOrDefault(x, 0) + 1);
// merge — cleaner frequency counting
freq.merge(x, 1, Integer::sum); // add 1 if exists, insert 1 if not
// computeIfAbsent — build adjacency list
Map<Integer, List<Integer>> graph = new HashMap<>();
graph.computeIfAbsent(u, k -> new ArrayList<>()).add(v);
// Iterate
for (Map.Entry<String, Integer> entry : map.entrySet()) {
entry.getKey(); entry.getValue();
}
for (var entry : map.entrySet()) { } // Java 10+
map.forEach((k, v) -> System.out.println(k + "=" + v));
// Keys and values only
for (String key : map.keySet()) { }
for (int val : map.values()) { }
// TreeMap — sorted by key, O(log n)
TreeMap<Integer, String> tm = new TreeMap<>();
tm.put(3, "c"); tm.put(1, "a"); tm.put(2, "b");
tm.firstKey() // 1
tm.lastKey() // 3
tm.floorKey(2) // 2
tm.ceilingKey(2) // 2
tm.lowerKey(2) // 1
tm.higherKey(2) // 3
tm.headMap(3) // keys < 3
tm.tailMap(2) // keys >= 2
Key tip: LinkedHashMap preserves insertion order and is the cleanest built-in way to implement LRU Cache in Java (a classic hard interview problem):
// LRU Cache — extend LinkedHashMap with accessOrder=true
int capacity = 3;
Map<Integer, Integer> lruCache = new LinkedHashMap<>(capacity, 0.75f, true) {
protected boolean removeEldestEntry(Map.Entry<Integer, Integer> eldest) {
return size() > capacity; // auto-evict least recently used
}
};
// get() and put() both update access order when accessOrder=true
8. PriorityQueue (Heap)
Java’s PriorityQueue is a min-heap by default. It’s the go-to for k-th largest/smallest problems, Dijkstra’s algorithm, and greedy scheduling.
import java.util.*;
// Min-heap (default)
PriorityQueue<Integer> minPQ = new PriorityQueue<>();
minPQ.offer(3); minPQ.offer(1); minPQ.offer(4);
minPQ.peek() // 1 (min, no removal)
minPQ.poll() // 1 (removes min)
minPQ.size()
minPQ.isEmpty()
// Max-heap
PriorityQueue<Integer> maxPQ = new PriorityQueue<>(Collections.reverseOrder()); // safe
// or overflow-safe lambda:
PriorityQueue<Integer> maxPQ2 = new PriorityQueue<>((a, b) -> Integer.compare(b, a));
// ⚠️ (a, b) -> b - a overflows near Integer.MIN_VALUE — avoid in interviews
// Min-heap by second element of int[] pair
PriorityQueue<int[]> pq = new PriorityQueue<>((a, b) -> Integer.compare(a[1], b[1]));
pq.offer(new int[]{1, 5});
pq.offer(new int[]{2, 3});
pq.poll() // {2, 3} (min by second element)
// Min-heap of int[] by first, then second
PriorityQueue<int[]> pq2 = new PriorityQueue<>((a, b) ->
a[0] != b[0] ? a[0] - b[0] : a[1] - b[1]);
// Build from collection — O(n)
PriorityQueue<Integer> fromList = new PriorityQueue<>(list);
// Common patterns
// K largest elements (use min-heap of size k)
PriorityQueue<Integer> kLargest = new PriorityQueue<>();
for (int x : arr) {
kLargest.offer(x);
if (kLargest.size() > k) kLargest.poll();
}
// kLargest now holds the k largest elements
Key tip: Use offer() instead of add() and poll() instead of remove() — they return null/false instead of throwing exceptions when the queue is empty or full.
9. Control Flow
// if / else if / else
if (x > 0) { }
else if (x == 0) { }
else { }
// Ternary
int result = (x > 0) ? x : -x;
// for loop
for (int i = 0; i < n; i++) { }
for (int i = n - 1; i >= 0; i--) { }
for (int i = 0; i < n; i += 2) { }
// Enhanced for (for-each)
for (int x : arr) { }
for (Map.Entry<String, Integer> e : map.entrySet()) { }
// while
while (condition) { }
// do-while
do { } while (condition);
// break / continue
for (int i = 0; i < n; i++) {
if (i == 5) break;
if (i % 2 == 0) continue;
}
// Labeled break (useful for nested loops)
outer:
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
if (grid[i][j] == target) break outer;
}
}
// Switch expression (Java 14+)
int day = 3;
String name = switch (day) {
case 1 -> "Mon";
case 2 -> "Tue";
case 3 -> "Wed";
default -> "Other";
};
10. Functions & Lambdas
// Basic method
int add(int a, int b) {
return a + b;
}
// Varargs
int sum(int... nums) {
int s = 0;
for (int n : nums) s += n;
return s;
}
sum(1, 2, 3); // 6
// Lambda (Java 8+)
// Runnable
Runnable r = () -> System.out.println("hello");
// Function<T, R>
import java.util.function.*;
Function<Integer, Integer> square = x -> x * x;
square.apply(5); // 25
// BiFunction<T, U, R>
BiFunction<Integer, Integer, Integer> add = (a, b) -> a + b;
// Predicate<T>
Predicate<Integer> isEven = x -> x % 2 == 0;
isEven.test(4); // true
// Comparator with lambda
Comparator<int[]> bySecond = (a, b) -> a[1] - b[1];
Comparator<String> byLength = Comparator.comparingInt(String::length);
Comparator<String> multiKey = Comparator
.comparingInt(String::length)
.thenComparing(Comparator.naturalOrder());
// Method references
list.forEach(System.out::println);
list.sort(Integer::compare);
// Recursion with helper (common interview pattern)
private int helper(int[] nums, int i, int[] memo) {
if (i >= nums.length) return 0;
if (memo[i] != -1) return memo[i];
return memo[i] = Math.max(
nums[i] + helper(nums, i + 2, memo),
helper(nums, i + 1, memo)
);
}
11. Sorting
import java.util.*;
int[] arr = {3, 1, 4, 1, 5};
// Primitive array — ascending (uses dual-pivot quicksort)
Arrays.sort(arr);
// Primitive array — descending (must box to Integer[])
Integer[] boxed = {3, 1, 4, 1, 5};
Arrays.sort(boxed, Collections.reverseOrder()); // safe
Arrays.sort(boxed, (a, b) -> b - a); // ⚠️ overflows if values span MIN/MAX_VALUE
// SAFE comparator — always prefer Integer.compare over subtraction:
Arrays.sort(boxed, (a, b) -> Integer.compare(b, a)); // descending, overflow-safe
// List sort
List<Integer> list = new ArrayList<>(Arrays.asList(3, 1, 4));
Collections.sort(list);
list.sort(Comparator.reverseOrder());
list.sort((a, b) -> Integer.compare(b, a)); // subtraction is unsafe for full int range
// Sort 2D array by first column (safe: values are small in practice, but Integer.compare is best practice)
int[][] pairs = {{1,3},{2,1},{3,2}};
Arrays.sort(pairs, (a, b) -> Integer.compare(a[0], b[0]));
// Sort by second column, then first descending on tie
Arrays.sort(pairs, (a, b) -> {
if (a[1] != b[1]) return Integer.compare(a[1], b[1]);
return Integer.compare(b[0], a[0]);
});
// Sort strings by length
String[] words = {"banana", "fig", "apple"};
Arrays.sort(words, Comparator.comparingInt(String::length));
// Sort by multiple keys using Comparator chain
Arrays.sort(words, Comparator
.comparingInt(String::length)
.thenComparing(Comparator.naturalOrder()));
// Reverse sorted order
Arrays.sort(words, Comparator
.comparingInt(String::length)
.reversed());
Key tip: You cannot use a Comparator directly on int[] via Arrays.sort(int[], Comparator) — the overload doesn’t exist for primitives. Use Integer[] or int[][] (2D) instead.
Overflow warning: (a, b) -> b - a overflows when values span Integer.MIN_VALUE to Integer.MAX_VALUE. Prefer Integer.compare(b, a) — it’s just as concise and always safe.
Comparator chain gotcha: .reversed() at the end of a chain reverses the entire comparator, not just the last key. To reverse only one key, apply .reversed() to that specific sub-comparator:
// Reverses EVERYTHING (length desc AND alpha desc):
Comparator.comparingInt(String::length).thenComparing(Comparator.naturalOrder()).reversed()
// Reverses only length (length desc, then alpha asc):
Comparator.comparingInt(String::length).reversed().thenComparing(Comparator.naturalOrder())
12. Binary Search
import java.util.*;
int[] arr = {1, 3, 5, 7, 9}; // must be sorted
// Built-in — returns index if found, or -(insertion point) - 1
int idx = Arrays.binarySearch(arr, 5); // 2
int neg = Arrays.binarySearch(arr, 4); // -3 (would go at index 2)
// Extract insertion point from negative result
int insertAt = -(neg) - 1; // 2
// TreeSet — floor/ceiling (sorted set operations)
TreeSet<Integer> ts = new TreeSet<>(Arrays.stream(arr).boxed().collect(Collectors.toList())); // toList() is Java 16+ — use collect() for compatibility
ts.floor(6) // 5 (largest <= 6)
ts.ceiling(6) // 7 (smallest >= 6)
// Manual binary search template
int lo = 0, hi = arr.length - 1;
while (lo <= hi) {
int mid = lo + (hi - lo) / 2; // avoids overflow
if (arr[mid] == target) return mid;
else if (arr[mid] < target) lo = mid + 1;
else hi = mid - 1;
}
// lo is the insertion point if not found
// leftmost occurrence
int leftBound(int[] arr, int target) {
int lo = 0, hi = arr.length;
while (lo < hi) {
int mid = lo + (hi - lo) / 2;
if (arr[mid] < target) lo = mid + 1;
else hi = mid;
}
return lo; // first index where arr[lo] >= target
}
// rightmost occurrence (exclusive upper bound)
int rightBound(int[] arr, int target) {
int lo = 0, hi = arr.length;
while (lo < hi) {
int mid = lo + (hi - lo) / 2;
if (arr[mid] <= target) lo = mid + 1;
else hi = mid;
}
return lo; // first index where arr[lo] > target
}
// Binary search on answer
int lo = 0, hi = (int) 1e9;
while (lo < hi) {
int mid = lo + (hi - lo) / 2;
if (feasible(mid)) hi = mid;
else lo = mid + 1;
}
// lo is the minimum feasible value
13. Useful Utility Methods
import java.util.*;
// Math
Math.max(a, b)
Math.min(a, b)
Math.abs(x)
Math.pow(2, 10) // 1024.0 (returns double)
Math.sqrt(16) // 4.0
Math.floor(3.7) // 3.0
Math.ceil(3.2) // 4.0
Math.log(x) // natural log
Math.log(x) / Math.log(2) // log base 2
// Integer utilities
Integer.toBinaryString(10) // "1010"
Integer.toHexString(255) // "ff"
Integer.bitCount(7) // 3 (number of set bits)
Integer.highestOneBit(6) // 4
Integer.numberOfLeadingZeros(8)
Integer.numberOfTrailingZeros(8)
Integer.reverse(x)
Integer.parseInt("1010", 2) // 10 (parse binary string)
// Collections utilities
Collections.sort(list)
Collections.reverse(list)
Collections.shuffle(list)
Collections.fill(list, 0)
Collections.min(list)
Collections.max(list)
Collections.frequency(list, val) // count occurrences
Collections.nCopies(5, 0) // [0, 0, 0, 0, 0]
Collections.unmodifiableList(list) // read-only view
Collections.swap(list, i, j)
// String utilities
String.valueOf(x) // any type → String
String.format("%05d", 42) // "00042"
String.format("%.2f", 3.14159) // "3.14"
// Streams — useful for concise transformations
import java.util.stream.*;
list.stream().filter(x -> x > 3).collect(Collectors.toList())
list.stream().map(x -> x * 2).collect(Collectors.toList())
list.stream().mapToInt(Integer::intValue).sum()
list.stream().distinct().sorted().collect(Collectors.toList())
Arrays.stream(arr).boxed().collect(Collectors.toList())
// Group elements by a key (very common in interview problems)
Map<Integer, List<Integer>> grouped =
list.stream().collect(Collectors.groupingBy(x -> x % 3));
// Word/element frequency count
Map<String, Long> freq =
Arrays.stream(words).collect(Collectors.groupingBy(w -> w, Collectors.counting()));
// Convert array to index→value map
Map<Integer, String> indexMap =
IntStream.range(0, words.length).boxed()
.collect(Collectors.toMap(i -> i, i -> words[i]));
// int[] ↔ List<Integer>
List<Integer> fromArr = Arrays.stream(arr).boxed().collect(Collectors.toList());
int[] fromList = list.stream().mapToInt(Integer::intValue).toArray();
Key tip — Java % vs true modulo: Java’s % returns negative results for negative dividends — unlike Python. Always use Math.floorMod when you need a non-negative remainder:
-7 % 3 // -1 ← Java's % (remainder, can be negative!)
Math.floorMod(-7, 3) // 2 ← true modulo (always non-negative)
Math.floorDiv(-7, 3) // -3 ← floor division
// Safe modular pattern when result must be positive:
long result = ((long) a % MOD + MOD) % MOD;
14. Bit Manipulation
// Bitwise operators
x & y // AND
x | y // OR
x ^ y // XOR
~x // NOT (bitwise complement)
x << k // left shift (= x * 2^k)
x >> k // arithmetic right shift (sign-preserving)
x >>> k // logical right shift (fills with 0)
// Common tricks
x & 1 // check if x is odd
x & (x - 1) // clear lowest set bit
x & (-x) // isolate lowest set bit
x ^ x // 0
x ^ 0 // x
// Power of 2 check
boolean isPow2 = (x > 0) && (x & (x - 1)) == 0;
// Count set bits
Integer.bitCount(x) // built-in
// Set / clear / toggle / read bit k
x |= (1 << k) // set bit k
x &= ~(1 << k) // clear bit k
x ^= (1 << k) // toggle bit k
(x >> k) & 1 // read bit k
// Iterate over all subsets of n elements
for (int mask = 0; mask < (1 << n); mask++) {
for (int i = 0; i < n; i++) {
if ((mask & (1 << i)) != 0) {
// element i is in this subset
}
}
}
Key tip: Java has two right-shift operators. >> is arithmetic (preserves sign bit — use for signed integers). >>> is logical (fills with 0 — safer for bit manipulation). In interviews, >>> avoids bugs with negative numbers.
15. Math & Numbers
// Modular arithmetic
long result = ((long) a * b) % MOD; // always cast before multiply
long result2 = (a % MOD + MOD) % MOD; // ensure positive mod
// Fast modular exponentiation
long powmod(long base, long exp, long mod) {
long result = 1;
base %= mod;
while (exp > 0) {
if ((exp & 1) == 1) result = result * base % mod;
base = base * base % mod;
exp >>= 1;
}
return result;
}
// GCD — Java has no Math.gcd(); write your own (unlike Python's math.gcd)
long gcd(long a, long b) {
return b == 0 ? a : gcd(b, a % b);
}
// LCM — divide first to avoid overflow; use long overload of gcd!
long lcm(long a, long b) {
return a / gcd(a, b) * b; // ✅ never cast long → int here
}
// Integer overflow guard
long safeAdd = (long) a + b;
long safeMul = (long) a * b;
// Random (for shuffling, not cryptography)
Random rand = new Random();
rand.nextInt(n) // [0, n)
rand.nextInt(hi - lo) + lo // [lo, hi)
16. Common Algorithm Patterns
These are the patterns behind the majority of Java coding interview questions. Internalize the templates — then practice applying them under time pressure.
Two Pointers
boolean twoSumSorted(int[] arr, int target) {
int l = 0, r = arr.length - 1;
while (l < r) {
int sum = arr[l] + arr[r];
if (sum == target) return true;
else if (sum < target) l++;
else r--;
}
return false;
}
Sliding Window
int maxSumSubarray(int[] arr, int k) {
int window = 0;
for (int i = 0; i < k; i++) window += arr[i];
int best = window;
for (int i = k; i < arr.length; i++) {
window += arr[i] - arr[i - k];
best = Math.max(best, window);
}
return best;
}
Fast & Slow Pointers
boolean hasCycle(ListNode head) {
ListNode slow = head, fast = head;
while (fast != null && fast.next != null) {
slow = slow.next;
fast = fast.next.next;
if (slow == fast) return true;
}
return false;
}
BFS Template
void bfs(Map<Integer, List<Integer>> graph, int start) {
Set<Integer> visited = new HashSet<>();
Queue<Integer> queue = new ArrayDeque<>(); // prefer ArrayDeque over LinkedList
queue.offer(start);
visited.add(start);
while (!queue.isEmpty()) {
int node = queue.poll();
for (int neighbor : graph.getOrDefault(node, List.of())) {
if (!visited.contains(neighbor)) {
visited.add(neighbor);
queue.offer(neighbor);
}
}
}
}
DFS Template (Recursive)
void dfs(Map<Integer, List<Integer>> graph, int node, Set<Integer> visited) {
visited.add(node);
for (int neighbor : graph.getOrDefault(node, List.of())) {
if (!visited.contains(neighbor))
dfs(graph, neighbor, visited);
}
}
Binary Search on Answer
int lo = 0, hi = (int) 1e9;
while (lo < hi) {
int mid = lo + (hi - lo) / 2;
if (feasible(mid)) hi = mid;
else lo = mid + 1;
}
// lo is the minimum feasible value
Dynamic Programming — Memoization
int[] memo;
int dp(int[] nums, int i) {
if (i >= nums.length) return 0;
if (memo[i] != -1) return memo[i];
return memo[i] = Math.max(
nums[i] + dp(nums, i + 2),
dp(nums, i + 1)
);
}
// Initialize in main:
// memo = new int[n]; Arrays.fill(memo, -1);
// dp(nums, 0);
Prefix Sum
int[] arr = {1, 2, 3, 4, 5};
int[] prefix = new int[arr.length + 1];
for (int i = 0; i < arr.length; i++)
prefix[i + 1] = prefix[i] + arr[i];
// Range sum query [l, r] inclusive:
int rangeSum = prefix[r + 1] - prefix[l];
Monotonic Stack
int[] nextGreater(int[] arr) {
int n = arr.length;
int[] result = new int[n];
Arrays.fill(result, -1);
Deque<Integer> stack = new ArrayDeque<>(); // stores indices
for (int i = 0; i < n; i++) {
while (!stack.isEmpty() && arr[i] > arr[stack.peek()]) {
result[stack.pop()] = arr[i];
}
stack.push(i);
}
return result;
}
Union-Find (Disjoint Set Union)
class UnionFind {
int[] parent, rank;
UnionFind(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 unite(int x, int y) {
int px = find(x), py = find(y);
if (px == py) return false;
if (rank[px] < rank[py]) { int t = px; px = py; py = t; }
parent[py] = px;
if (rank[px] == rank[py]) rank[px]++;
return true;
}
boolean connected(int x, int y) {
return find(x) == find(y);
}
}
Standard Node Definitions
Many LeetCode problems use these classes. If they’re not pre-provided, write them from scratch:
// Singly linked list node
class ListNode {
int val;
ListNode next;
ListNode(int val) { this.val = val; }
ListNode(int val, ListNode next) { this.val = val; this.next = next; }
}
// Binary tree node
class TreeNode {
int val;
TreeNode left, right;
TreeNode(int val) { this.val = val; }
TreeNode(int val, TreeNode left, TreeNode right) {
this.val = val; this.left = left; this.right = right;
}
}
Building a Graph from an Edge List
BFS/DFS templates assume the graph already exists — here’s how to build it:
// Undirected graph
Map<Integer, List<Integer>> graph = new HashMap<>();
for (int[] edge : edges) {
graph.computeIfAbsent(edge[0], k -> new ArrayList<>()).add(edge[1]);
graph.computeIfAbsent(edge[1], k -> new ArrayList<>()).add(edge[0]);
}
// Directed graph (remove the second computeIfAbsent line)
// Weighted graph
for (int[] edge : edges) { // {from, to, weight}
graph.computeIfAbsent(edge[0], k -> new ArrayList<>()).add(new int[]{edge[1], edge[2]});
}
DFS Template (Iterative)
void dfsIterative(Map<Integer, List<Integer>> graph, int start) {
Set<Integer> visited = new HashSet<>();
Deque<Integer> stack = new ArrayDeque<>();
stack.push(start);
while (!stack.isEmpty()) {
int node = stack.pop();
if (visited.contains(node)) continue;
visited.add(node);
for (int neighbor : graph.getOrDefault(node, List.of())) {
if (!visited.contains(neighbor))
stack.push(neighbor);
}
}
}
Backtracking Template
Use for subsets, permutations, combinations, N-Queens, and word search:
List<List<Integer>> result = new ArrayList<>();
void backtrack(int[] nums, int start, List<Integer> path) {
result.add(new ArrayList<>(path)); // ⚠️ snapshot — NEVER add path directly!
for (int i = start; i < nums.length; i++) {
path.add(nums[i]); // choose
backtrack(nums, i + 1, path); // explore
path.remove(path.size() - 1); // undo (backtrack)
}
}
Key tip: Always add new ArrayList<>(path), not path itself. Adding path directly means every snapshot in result will point to the same mutating list.
Trie (Prefix Tree)
Used for word search, autocomplete, and prefix-matching problems:
class TrieNode {
Map<Character, TrieNode> children = new HashMap<>();
boolean isEnd = false;
}
class Trie {
TrieNode root = new TrieNode();
void insert(String word) {
TrieNode node = root;
for (char c : word.toCharArray()) {
node.children.putIfAbsent(c, new TrieNode());
node = node.children.get(c);
}
node.isEnd = true;
}
boolean search(String word) {
TrieNode node = root;
for (char c : word.toCharArray()) {
if (!node.children.containsKey(c)) return false;
node = node.children.get(c);
}
return node.isEnd;
}
boolean startsWith(String prefix) {
TrieNode node = root;
for (char c : prefix.toCharArray()) {
if (!node.children.containsKey(c)) return false;
node = node.children.get(c);
}
return true;
}
}
Quick Reference Cheat Sheet
| Task | Snippet |
|---|---|
| Max/min | Math.max(a, b) / Math.min(a, b) |
| Sort array | Arrays.sort(arr) |
| Sort descending | Arrays.sort(boxed, Collections.reverseOrder()) |
| Sort list (safe) | list.sort((a, b) -> Integer.compare(b, a)) |
| Reverse list | Collections.reverse(list) |
| Sum array | Arrays.stream(arr).sum() |
| Frequency map | freq.merge(x, 1, Integer::sum) |
| Contains key | map.containsKey(k) |
| Safe default | map.getOrDefault(k, 0) |
| Build adjacency list | graph.computeIfAbsent(u, k -> new ArrayList<>()).add(v) |
| Min-heap | new PriorityQueue<>() |
| Max-heap | new PriorityQueue<>(Collections.reverseOrder()) |
| TreeSet floor | ts.floor(val) |
| Set bit k | x |= (1 << k) |
| Count set bits | Integer.bitCount(x) |
| String → int | Integer.parseInt(s) |
| Int → string | String.valueOf(x) |
| Build string | new StringBuilder().append(...).toString() |
| Infinity | Integer.MAX_VALUE / Long.MAX_VALUE |
| True modulo | Math.floorMod(a, b) (never negative) |
| Group by key | stream.collect(Collectors.groupingBy(...)) |
| Frequency count | stream.collect(Collectors.groupingBy(..., Collectors.counting())) |
| LRU Cache | LinkedHashMap with accessOrder=true |
| Overflow-safe compare | Integer.compare(a, b) not a - b |
Related Interview Topics
Comfortable with Java collections? The next step is mapping these tools to the core problem categories in technical interviews:
- Arrays & Hashing —
HashMapfrequency maps,HashSetfor O(1) lookup, two-sum variants - Binary Search —
Arrays.binarySearch,TreeSetfloor/ceiling, binary search on answer - Trees & Graphs — BFS with
Queue/LinkedList, DFS with recursion orDequeas stack - Dynamic Programming — memoization with
int[]orHashMap, tabulation with 1D/2D arrays - Heaps & Greedy —
PriorityQueuewith custom comparators, k-th largest/smallest patterns
Each of these patterns has dedicated walkthroughs on the Intervu blog.
Practice Under Real Interview Conditions
Knowing the Java Collections framework is a prerequisite — but what interviewers actually evaluate is how you think through a problem under time pressure, communicate your reasoning, and handle follow-up questions.
That’s a skill you build through practice, not by reading. Intervu gives you AI-powered mock coding interviews that feel like the real thing — timed, with follow-up questions, and instant feedback on your approach, your edge case handling, and your code quality.
Ready to test yourself? Start a free mock interview on Intervu →