Java for Coding Interviews: The Standard Library That Bites Back

- Java collections decision table: use
HashMapfor O(1) lookup,TreeMapfor sorted key navigation,PriorityQueuefor heaps, andArrayDequefor both stack and queue getOrDefaultandcomputeIfAbsentare the two HashMap idioms you'll write in nearly every frequency-counting or grouping problemInteger ==breaks outside -128 to 127: objects in that range are cached making==look correct; always use.equals()or compare with primitiveint- Comparator subtraction overflow is the most common silent bug:
(a, b) -> b - awraps on large negatives; useInteger.compare(b, a)every time Arrays.sort(int[])accepts no comparator: convert toInteger[]or sort an index array when you need custom orderingStringBuilderin loops is required for O(n) string building; concatenation with+allocates a new string each iteration and runs O(n²)- Autoboxing NPE:
map.get("missing")returnsnulland unboxing it into anintthrows immediately; always usegetOrDefault
You're going to use Java. Maybe your CS degree burned it into your fingers. Maybe you live in a Spring Boot codebase and Python feels like a foreign country. Either way, you've made your choice, and now you need to survive the interview.
The algorithm is the same in any language. What gets Java candidates is the details: which collection to reach for, which idioms to write without thinking, and which language-specific traps are waiting, patiently, to flip an otherwise-correct solution at the worst possible moment.
Let's go through it.
The Collection Framework in One Table
Java's java.util package gives you almost everything you need without installing anything. Here's the decision map:
| Need | Use | Notes |
|---|---|---|
| Key-value store, O(1) ops | HashMap | Unordered |
| Sorted key-value store | TreeMap | O(log n) ops, red-black tree |
| O(1) membership test | HashSet | Unordered |
| Sorted membership | TreeSet | O(log n), deduplicates |
| Priority queue (min-heap) | PriorityQueue | Max-heap needs comparator |
| Stack | ArrayDeque | Not Stack (legacy, synchronized) |
| Queue / BFS | ArrayDeque | Not LinkedList |
| Dynamic array | ArrayList | List.of(...) for fixed |
| Sorted list ops, prefix sums | int[] + binary search | Primitives, no autoboxing |
Memorize this table. The wrong collection is usually fine on small examples but embarrassing in a follow-up question about performance or thread safety.
HashMap: Your Interview Workhorse
Almost every problem involving frequency counting, grouping, or fast lookup ends at HashMap. The hash map time complexity is O(1) average for get and put, O(n) worst case when all keys collide into one bucket. Know these three methods beyond just put and get:
// Frequency counting map.getOrDefault(key, 0) // avoids a null check map.put(key, map.getOrDefault(key, 0) + 1); // Conditional insert (only if key absent) map.putIfAbsent(key, new ArrayList<>()); // Compute pattern (frequency in one line) map.merge(key, 1, Integer::sum);
getOrDefault is the one you'll actually write under pressure. Write it ten times until it's automatic.
For grouping problems (like anagram grouping), the pattern is always computeIfAbsent to build the list lazily:
Map<String, List<String>> groups = new HashMap<>(); for (String word : words) { char[] chars = word.toCharArray(); Arrays.sort(chars); String key = new String(chars); groups.computeIfAbsent(key, k -> new ArrayList<>()).add(word); }
TreeMap: When Order Matters
HashMap is O(1) but orderless. TreeMap keeps keys sorted and gives you O(log n) operations plus navigation methods worth knowing:
TreeMap<Integer, Integer> map = new TreeMap<>(); map.floorKey(target) // largest key <= target, null if none map.ceilingKey(target) // smallest key >= target, null if none map.firstKey() // minimum key map.lastKey() // maximum key map.headMap(k) // submap with keys strictly < k map.tailMap(k) // submap with keys >= k
If the problem mentions a sorted range of keys, or sliding-window-by-value with ordered retrieval, reach for TreeMap before you reach for a sorted array. Classic examples: Sliding Window Maximum solved with a TreeMap of value counts, Meeting Rooms II with sorted event times.
PriorityQueue: Default Min-Heap, and the Max-Heap Trap
Java's PriorityQueue is a min-heap. The smallest element comes out first. For a max-heap, reverse the comparator:
// Min-heap (default) PriorityQueue<Integer> minHeap = new PriorityQueue<>(); // Max-heap PriorityQueue<Integer> maxHeap = new PriorityQueue<>(Collections.reverseOrder()); // Custom comparator (sort by second element descending) PriorityQueue<int[]> pq = new PriorityQueue<>((a, b) -> b[1] - a[1]);
Wait. That last line has a bug. Read on.
Key methods: offer(x) to insert O(log n), poll() to remove the top O(log n), peek() to read the top O(1). Use offer and poll, not add and remove. The add/remove variants throw exceptions on failure. offer/poll return false/null. Exceptions in an interview are a surprise nobody needs.
ArrayDeque: Forget Stack Exists
Java has a Stack class. Forget it exists. It's legacy, it's synchronized (meaning it locks on every operation for no reason you care about), and it inherits from Vector. Use ArrayDeque for both stacks and queues:
Deque<Integer> stack = new ArrayDeque<>(); stack.push(x); // push to front stack.pop(); // remove from front stack.peek(); // read front Deque<Integer> queue = new ArrayDeque<>(); queue.offer(x); // add to back queue.poll(); // remove from front queue.peek(); // read front
ArrayDeque beats LinkedList on every benchmark because it's backed by a contiguous array. Cache locality matters. The array vs linked list performance difference is real and measurable, even on interview-scale inputs.
The Five Traps That Will Wreck You
These are not theoretical edge cases. They appear in real interviews. Several are subtle enough that you won't notice until you trace through an example and something doesn't add up. Your interviewer, however, will notice immediately.
Trap 1: Integer == Comparison
Java caches Integer objects for values from -128 to 127. Within that range, == compares values correctly by coincidence. Outside it, == compares object references and you get to explain why your solution thinks 128 doesn't equal 128.
Integer a = 128; Integer b = 128; System.out.println(a == b); // false (!!) System.out.println(a.equals(b)); // true
Always use .equals() when comparing Integer objects. This bites you most often in map-based solutions where you pull a value with .get() and compare it to another Integer. Use int primitives wherever possible, or Objects.equals(a, b) which is null-safe.
Trap 2: Comparator Subtraction Overflow
This one is everywhere in interview code. It looks fine. It isn't.
// WRONG: overflows for large negative values PriorityQueue<Integer> pq = new PriorityQueue<>((a, b) -> b - a); // CORRECT PriorityQueue<Integer> pq = new PriorityQueue<>((a, b) -> Integer.compare(b, a));
If b is Integer.MIN_VALUE and a is positive, b - a wraps around to a large positive number, flipping the comparison. Use Integer.compare(), always. It's two more characters that prevent a category of bug.
Trap 3: Autoboxing NullPointerException
This one produces the most confusing stack traces. An NPE on a line that doesn't dereference anything.
Map<String, Integer> freq = new HashMap<>(); int count = freq.get("missing"); // NullPointerException: unboxing null
freq.get("missing") returns null. Java tries to unbox it to int. Boom.

You were warned. The map was empty. You ran it anyway.
Fix: getOrDefault("missing", 0), or store the result in Integer first and null-check.
Trap 4: Arrays.sort() Is In-Place
Arrays.sort() mutates the array. If you need a sorted copy while keeping the original for index lookups, copy first.
int[] sorted = nums.clone(); // or Arrays.copyOf(nums, nums.length) Arrays.sort(sorted);
Collections.sort() also mutates the list. Neither returns a new collection. Finding this out mid-interview by accidentally destroying your input is not fun.
Trap 5: Sorting Integers With a Primitive Array
Arrays.sort(int[]) uses a dual-pivot quicksort. It doesn't accept a comparator. If you need a custom sort order on integers, sort an Integer[] (boxed), or sort indices into the original array.
// Sort indices by value descending Integer[] indices = new Integer[n]; for (int i = 0; i < n; i++) indices[i] = i; Arrays.sort(indices, (a, b) -> Integer.compare(nums[b], nums[a]));
This pattern shows up constantly in problems like "sort people by height, break ties by name."
String Operations: Don't Build in a Loop
String concatenation in a loop is O(n²) because each + allocates a new String. Use StringBuilder whenever you're building a string character by character or across iterations.
StringBuilder sb = new StringBuilder(); for (char c : chars) { sb.append(c); } String result = sb.toString();
Other useful idioms: String.valueOf(c) to convert a char to String, s.toCharArray() to get a mutable char array, s.charAt(i) for O(1) access. For sorting characters in a string: toCharArray(), sort the array, then new String(arr).
Sorting Custom Objects
Lambda comparators with Comparator.comparing chain well for multi-key sorts:
// Sort by length, then lexicographically list.sort(Comparator.comparingInt(String::length).thenComparing(Comparator.naturalOrder())); // Reverse on one key list.sort(Comparator.comparingInt((String s) -> s.length()).reversed());
Chaining .thenComparingInt keeps the intent readable and sidesteps the subtraction overflow trap entirely.
One more thing: Arrays.sort on an Object[] or Integer[] uses Timsort, a stable sort. Arrays.sort on a primitive int[] uses dual-pivot quicksort, faster but not stable. If you need stable sorting for index-tracking, box your integers. For the tradeoffs, see merge sort vs quicksort.
Java vs Python: When to Actually Pick Java

This is what Python devs think happens every time you open a Java file. They're not entirely wrong.
For most candidates at most companies, Python is the faster interview choice: shorter, fewer type-related edge cases, richer built-ins. But Java is the right call in a few situations.
Pick Java if:
- You code Java at work daily and haven't touched Python in years. Fluency beats library size. Switching languages mid-prep is a gamble you can't afford.
- The company's stack is Java-heavy (Android, Spring, financial systems). Your interviewers will read your code more fluidly.
- The role is explicitly backend Java or Android. Matching the language signals domain awareness.
Pick the language where you can implement a linked list reversal at 9 AM without thinking about syntax. The algorithm is the hard part. The language should be invisible. A voice-based mock interview on SpaceComplexity can surface whether your language fluency is holding you back, especially when you're narrating the solution out loud and typing at the same time.
For language-agnostic prep, see best language for coding interviews.
Quick Reference Cheat Sheet
// HashMap Map<K,V> m = new HashMap<>(); m.getOrDefault(k, def); m.putIfAbsent(k, v); m.merge(k, 1, Integer::sum); m.computeIfAbsent(k, x -> new ArrayList<>()).add(v); // TreeMap TreeMap<K,V> t = new TreeMap<>(); t.floorKey(k); t.ceilingKey(k); t.firstKey(); t.lastKey(); // PriorityQueue PriorityQueue<Integer> min = new PriorityQueue<>(); PriorityQueue<Integer> max = new PriorityQueue<>(Collections.reverseOrder()); // min.offer(), min.poll(), min.peek() // ArrayDeque (stack) Deque<T> stack = new ArrayDeque<>(); stack.push(x); stack.pop(); stack.peek(); // ArrayDeque (queue) Deque<T> q = new ArrayDeque<>(); q.offer(x); q.poll(); q.peek(); // Sorting Arrays.sort(arr); // primitive int[], in-place Arrays.sort(arr, (a,b) -> Integer.compare(a,b)); // Integer[] only Collections.sort(list, comparator); // List<T>, in-place // String StringBuilder sb = new StringBuilder(); sb.append(c); sb.toString(); char[] arr = s.toCharArray(); Arrays.sort(arr); new String(arr); // Copy array before sorting int[] copy = nums.clone();