Kotlin for Coding Interviews: The Cheat Sheet You'll Actually Use

- Fluency beats features: use Kotlin if you write it daily, especially for Android roles; only switch to Python if you're more comfortable there
ArrayDequereplaces bothjava.util.StackandLinkedList: O(1) amortized, no nulls, no synchronization overheadPriorityQueueis a min-heap by default: passreverseOrder()for max-heap;compareBychains cleanly for multi-criteria orderingIntoverflows silently at 2,147,483,647: default toLongfor products, path sums, or any constraint near 10^9IntArrayis unboxed (int[]),Array<Int>is boxed (Integer[]): useIntArrayfor DP arrays, graphs, and anything performance-sensitivetailrecgives free tail-call optimization: the compiler rewrites eligible recursion into a loop, eliminating stack overflow riskTreeMapbeats HashMap + manual sorting:floorKey/ceilingKey/firstKey/lastKeyhandle nearest-value and range queries in O(log n)
Your interview is in 48 hours. You've written Kotlin for two years. Some well-meaning recruiter told you Python is the language for LeetCode, and now you're staring at a heapq tutorial at midnight, learning a new standard library in the language you half-know.
Don't do that. Use the language you already know cold.
Fluency beats features. You know the footguns. You'll type faster. You'll spend zero mental overhead on syntax and all of it on the algorithm, which is the part that gets you hired.
This Kotlin for coding interviews guide covers the data structures that matter, the gotchas that trip people up, and a cheat sheet you can internalize in one session.
Should You Use Kotlin for Coding Interviews?
Use Kotlin if you're interviewing for Android roles or already write it daily. In a 45-minute interview, a language you know cold is worth more than a "better" language you half-know.
If you're targeting general SWE roles at FAANG, Python wins on raw conciseness. What takes three lines in Python often takes seven in Kotlin. That matters when you're down to eight minutes and the interviewer is watching your cursor blink. But if Python is foreign to you and Kotlin isn't, run with Kotlin. Most interviewers don't care which language you use as long as you can explain what you're doing. (There will be one who cares deeply. Most won't.)
Kotlin runs on the JVM, so the standard library is a mix of Kotlin-native types (ArrayDeque, MutableList) and Java interop types (PriorityQueue, TreeMap). The mixing is mostly invisible, but it creates a few traps worth knowing.
The Collection Decision Table
| Need | Use | Avoid |
|---|---|---|
| Stack or queue | ArrayDeque | java.util.Stack, LinkedList |
| Min-heap / max-heap | PriorityQueue | Manual sorting |
| Sorted map with navigation | TreeMap | HashMap + sorting later |
| Sorted set | TreeSet | HashSet + sorting later |
| Frequency map | mutableMapOf() + getOrDefault | Manual if-else every time |
| Resizable array | mutableListOf() or ArrayList() | Array<Int> for growing lists |
| Fixed-size primitive array | IntArray(n) | Array<Int> for primitives |
Stack and Queue: Never Use java.util.Stack Again
The old Java Stack class is synchronized. It acquires a mutex lock on every push and pop, in case your binary tree traversal needed to be thread-safe for some reason. You don't need that, and you don't need its ancient Vector-based API. Kotlin's ArrayDeque is the right answer for both stacks and queues.
val stack = ArrayDeque<Int>() stack.addLast(1) // push stack.removeLast() // pop stack.last() // peek val queue = ArrayDeque<Int>() queue.addLast(1) // enqueue queue.removeFirst() // dequeue queue.first() // peek front
ArrayDeque is O(1) amortized for all four operations and doesn't allow nulls, which eliminates a category of subtle bugs. The no-nulls rule matters: if your graph has nullable node values, store them in a wrapper, not directly in the deque.
For BFS you can also use java.util.LinkedList as a queue, but ArrayDeque is faster in practice because of cache locality. Sequential memory wins over pointer chasing, same as arrays beat linked lists in traversal benchmarks.
Priority Queues Done Right
Kotlin doesn't have its own priority queue, so you use Java's. It's a min-heap by default.
// Min-heap (default) val minHeap = PriorityQueue<Int>() // Max-heap val maxHeap = PriorityQueue<Int>(reverseOrder()) // Custom comparator: sort pairs by second element descending val pq = PriorityQueue<Pair<Int, Int>>(compareByDescending { it.second }) minHeap.offer(3) val smallest = minHeap.poll() // removes and returns min val peek = minHeap.peek() // looks without removing
The bug that has derailed more interview solutions than null pointers: PriorityQueue is a min-heap by default. You need a max-heap. You pass nothing. Your k-th largest answer comes out backward and correct-looking on small test cases. Pass reverseOrder() or compareByDescending { it }. Do not negate values as a workaround unless you can explain why the domain allows it.
peek() on an empty queue returns null rather than throwing. Guard with isNotEmpty() before you poll.
When you need to sort by multiple criteria, compareBy composes cleanly:
// Sort by frequency ascending, then by value descending on tie val pq = PriorityQueue<Int>(compareBy<Int> { freq[it] }.thenByDescending { it })
TreeMap for Ordered Operations
TreeMap is a red-black tree. It keeps keys sorted and gives you O(log n) navigation operations that HashMap simply can't do.
val map = TreeMap<Int, String>() map[5] = "five" map[3] = "three" map[8] = "eight" map.floorKey(6) // 5 (largest key <= 6) map.ceilingKey(6) // 8 (smallest key >= 6) map.firstKey() // 3 map.lastKey() // 8 map.headMap(6) // submap with keys < 6 map.tailMap(6) // submap with keys >= 6
Reach for TreeMap any time the problem involves "nearest value", "next greater element in a set", or "sliding window with range queries". The navigation methods replace binary search on a sorted array you'd otherwise have to maintain manually.
TreeMap uses about 3x more memory than HashMap for the same keys. Rarely a constraint in a 45-minute interview, but worth mentioning when you walk through trade-offs.
The Int Overflow Trap
Kotlin's Int is 32-bit. It overflows at 2,147,483,647. You hit this constantly: two-sum with large values, path sums in trees, product subarray.
Default to Long whenever the problem involves products, sums over large arrays, or constraints near 10^9.
// Wrong: overflows silently var product = 1 for (n in nums) product *= n // wraps to garbage if nums has large values // Right var product = 1L for (n in nums) product *= n.toLong()
The product of [100000, 100000, 100000] is 10^15. Kotlin's Int tops out at around 2.1 billion. The runtime won't warn you. It'll quietly wrap around to a negative number and look fine on small test cases. Kotlin doesn't silently promote Int to Long in arithmetic: if you multiply two Int values, the result is Int. Cast at least one operand first: n.toLong() * m.
IntArray vs Array<Int>: The Boxing Problem
val a = IntArray(5) // int[] on the JVM (no boxing, fast) val b = Array<Int>(5) { 0 } // Integer[] on the JVM (boxed, slower)
For performance-sensitive algorithms (sorting, DP arrays, graphs), use IntArray or LongArray. Array<Int> boxes each value into an Integer object, which costs allocation and pointer-chasing.
Two places it bites you:
Arrays.sort(intArray)uses dual-pivot quicksort (fast, unstable).Array<Int>.sort()uses Timsort (stable). If you need stable sort, usesortedArray().- Passing
IntArrayto a function expectingArray<Int>doesn't compile. Convert with.toTypedArray().
Sorting Without the Comparator Headache
Kotlin's sorting API is friendlier than Java's. You rarely need to write a Comparator explicitly.
list.sort() // in-place ascending list.sortDescending() // in-place descending val sorted = list.sortedBy { it } // returns new list data class Point(val x: Int, val y: Int) points.sortWith(compareBy({ it.x }, { it.y })) // x first, y breaks ties
sortWith(compareBy(...)) chains are stable and readable. One trap: sortedBy and sortedWith return a new list. For in-place, use sortBy and sortWith (no "ed").
Four Stdlib Functions That Actually Save Lines
getOrDefault is the clean frequency map pattern:
val freq = mutableMapOf<Char, Int>() for (c in s) freq[c] = freq.getOrDefault(c, 0) + 1
getOrPut handles nested structures without a null check:
// Group words by anagram key (no if-absent boilerplate) val grouped = mutableMapOf<String, MutableList<String>>() grouped.getOrPut(key) { mutableListOf() }.add(word)
zipWithNext compares adjacent pairs without manual indexing:
val isNonDecreasing = nums.zipWithNext().all { (a, b) -> a <= b }
groupBy collapses a list into a map by key in one line:
val anagramGroups = words.groupBy { it.toCharArray().sorted().joinToString("") }
These four cover a surprising amount of ground. Know them cold, because the Kotlin stdlib has 600+ extension functions and the interview room has no internet.

The only skill that gets banned from the interview room. Know your getOrDefault from memory.
tailrec: One Keyword, Free TCO
Kotlin supports tail call optimization via the tailrec modifier. The compiler rewrites eligible recursive functions into loops, eliminating stack overflow risk.
tailrec fun factorial(n: Int, acc: Long = 1L): Long = if (n <= 1) acc else factorial(n - 1, acc * n)
The function must call itself as the very last operation with no further computation on the result. return 1 + tailrecFn(n - 1) is not tail-recursive and tailrec won't help. The compiler warns you when the keyword is present but the structure isn't actually eligible. Pay attention to that warning.
This matters for deep tree traversals where recursion depth could hit the JVM's default stack limit.
When Kotlin Slows You Down
No language is free. Kotlin has a few interview-specific costs worth knowing before you walk in.
Null safety is a tax in interview code. The ?. and ?: operators are great in production, but in a 45-minute session you're writing throwaway code. Every ?.let { } and ?: return adds cognitive load when you're already tracking two pointers and an edge case. If you find yourself writing node?.left?.let { queue.add(it) }, just write explicit null checks.
The standard library is large and hard to recall under pressure. If you can't remember whether it's removeFirst() or pollFirst() or pop(), you'll waste time. Stick to a small, memorized subset. The cheat sheet below is exactly that.
Kotlin doesn't have a built-in heap as ergonomic as Python's heapq. You're using Java's PriorityQueue, which requires explicit comparators and has the min/max footgun. Not a dealbreaker, but more typing than heapq.heappush.
Interviewers who don't know Kotlin may misread your code. Rare at companies that use Kotlin internally, but worth asking your recruiter about upfront.

The ?.let {} syntax that works great at your day job becomes a liability when the clock is running.
Quick-Reference Cheat Sheet
// ---- Stack / Queue ---- val stack = ArrayDeque<Int>() stack.addLast(x); stack.removeLast(); stack.last() val queue = ArrayDeque<Int>() queue.addLast(x); queue.removeFirst(); queue.first() // ---- Heap ---- val minH = PriorityQueue<Int>() val maxH = PriorityQueue<Int>(reverseOrder()) val custH = PriorityQueue<Pair<Int,Int>>(compareBy { it.first }) heap.offer(x); heap.poll(); heap.peek() // ---- Sorted Map ---- val tm = TreeMap<Int, String>() tm.floorKey(k); tm.ceilingKey(k); tm.firstKey(); tm.lastKey() // ---- Arrays ---- val arr = IntArray(n) { 0 } // primitive, fast val arr2 = Array(n) { mutableListOf<Int>() } // object array // ---- Frequency Map ---- val freq = mutableMapOf<Int, Int>() for (x in arr) freq[x] = freq.getOrDefault(x, 0) + 1 // ---- Sorting ---- arr.sort() // IntArray, in-place list.sortWith(compareBy({ it.x }, { it.y })) // ---- Overflow guard ---- val product = a.toLong() * b // always cast before multiply // ---- Ranges ---- if (i in 0 until n) { } // bounds check for (i in n - 1 downTo 0) { } // reverse loop // ---- tailrec ---- tailrec fun dfs(node: TreeNode?, acc: Int): Int = ...
Key Takeaways
- Use Kotlin if you're already fluent, especially for Android roles. Switch to Python if you're less comfortable with it.
ArrayDequereplaces bothjava.util.StackandLinkedListfor stacks and queues.PriorityQueueis a min-heap by default. PassreverseOrder()for a max-heap.Intoverflows silently. Default toLongfor products and large sums.IntArrayis unboxed and fast.Array<Int>is boxed. Know the difference.tailrecgives you free TCO for eligible recursive functions.
If you want to pressure-test these patterns under actual interview conditions, SpaceComplexity runs voice-based DSA mock interviews with rubric feedback, including scoring on communication and edge case handling, the two dimensions LeetCode grinding doesn't train.
Further Reading
- Kotlin Collections Overview: official docs for
MutableList,MutableMap,MutableSet - Kotlin Standard Library API Reference: canonical reference for every stdlib function
- Java PriorityQueue on Android Developers: Kotlin-idiomatic PriorityQueue docs
- Kotlin Docs: Ordering:
compareBy,thenBy,sortedWithin depth - Tech Interview Handbook: Language Choice: language tradeoffs for coding interviews
Also useful from this blog:
- Java for Coding Interviews covers the JVM gotchas Kotlin inherits, including
Integercaching, comparator overflow, andArrays.sortstability. - Best Language for Coding Interviews breaks down the fluency vs feature trade-off in more depth.
- Android Developer Interview: The DSA You Actually Need covers what Kotlin-specific interview rounds actually look like.
- DSA for Backend Engineers maps the patterns backend roles test most often, regardless of language.