Collections
The Collections framework — List/Set/Map types, ArrayList vs LinkedList, comparators, and copies.
14 questions
JuniorTheoryVery commonHow do ArrayList and LinkedList differ in their internal structure?
How do ArrayList and LinkedList differ in their internal structure?
ArrayList is backed by a resizable array, so random access by index is O(1), but inserting or removing in the middle shifts later elements and costs O(n). LinkedList is a doubly-linked list of nodes, so adding or removing at the ends is O(1), but reaching an index means walking the chain, giving O(n) access. Pick ArrayList for index-heavy reads, LinkedList for frequent end edits.
Common mistakes
- ✗Believing
LinkedList.get(i)is O(1) — it walks the chain, so indexed loops over it are O(n²) - ✗Forgetting
ArrayListoccasionally resizes and copies, making single adds amortized rather than flat - ✗Choosing
LinkedListfor general use whenArrayListis faster for most real workloads
Follow-up questions
- →Why does removing from the middle of a
LinkedListstill cost O(n) despite O(1) node unlinking? - →What is amortized O(1) for an
ArrayListadd, and where does the n-cost spike come from?
JuniorTheoryVery commonWhat is the Java Collections framework, and what does it provide?
What is the Java Collections framework, and what does it provide?
The Collections framework is a unified architecture for storing and manipulating groups of objects. It pairs core interfaces — Collection, List, Set, Map, Queue — with ready implementations like ArrayList and HashMap, plus reusable algorithms in the Collections utility class. This gives one consistent API, letting you swap implementations without rewriting calling code.
Common mistakes
- ✗Confusing the framework as a whole with the single
Collectionsutility class of static helpers - ✗Assuming
MapextendsCollection— it is a separate root interface in the hierarchy - ✗Thinking the framework only ships interfaces, missing the concrete implementations and algorithms
Follow-up questions
- →Why is
Mapnot a subtype of theCollectionroot interface? - →How does coding to an interface ease swapping one implementation for another?
JuniorTheoryVery commonWhat is the difference between a List and a Set in Java?
What is the difference between a List and a Set in Java?
A List is an ordered, positional sequence: it keeps insertion order, lets you access elements by index, and permits duplicate values. A Set models a mathematical set: it rejects duplicate elements, so each value appears at most once, and it is typically unordered (HashSet) — though TreeSet keeps elements sorted and LinkedHashSet preserves insertion order. Choose a List for sequences, a Set for uniqueness.
Common mistakes
- ✗Assuming every
Setis unordered —TreeSetis sorted andLinkedHashSetkeeps insertion order - ✗Expecting
get(int index)on aSet, which has no positional access - ✗Forgetting a
Setsilently drops a duplicate add rather than throwing
Follow-up questions
- →What two methods decide whether a
HashSettreats two elements as duplicates? - →When would you prefer a
LinkedHashSetover a plainHashSet?
JuniorTheoryCommonWhat is the difference between Collection and Collections in Java?
What is the difference between Collection and Collections in Java?
Collection (singular) is the root interface of the hierarchy that List, Set, and Queue extend — it declares operations like add, remove, and size. Collections (plural) is a utility class holding only static helper methods such as sort, reverse, shuffle, synchronizedList, and unmodifiableList. So one is the interface you implement, the other is a toolbox you call.
Common mistakes
- ✗Calling
Collectionsmethods on an instance instead of statically through the class name - ✗Swapping the two names because they differ only by a trailing
s - ✗Thinking
Collectionsis an interface you can implement rather than a final utility class
Follow-up questions
- →What does
Collections.unmodifiableListreturn, and what happens on a write to it? - →Why are the methods on the
Collectionsclass declared static?
MiddleTheoryCommonWhat is the difference between Comparable and Comparator in Java?
What is the difference between Comparable and Comparator in Java?
Comparable defines a type's single natural ordering: the class implements compareTo(other) on itself, so Collections.sort knows the default order. Comparator is a separate object defining one of many external orderings via compare(a, b), letting you sort the same type by different keys without touching its source. Use Comparable for the one obvious order, Comparator for alternative or third-party orderings.
Common mistakes
- ✗Returning a boolean from
compareTo/compareinstead of a negative, zero, or positive int - ✗Making natural ordering inconsistent with
equals, breakingTreeSet/TreeMapbehaviour - ✗Putting alternative orderings into
compareToinstead of separateComparatorobjects
Follow-up questions
- →Why should a type's natural ordering be consistent with its
equalsmethod? - →How do
Comparator.comparingandthenComparingbuild a multi-key ordering?
MiddleTheoryCommonWhat is the difference between HashMap and Hashtable in Java?
What is the difference between HashMap and Hashtable in Java?
HashMap is unsynchronized, allows one null key and any number of null values, and is faster — the standard choice for single-threaded code. Hashtable is a legacy class whose methods are synchronized, so it locks the whole table per call, and it forbids null keys and values, throwing NullPointerException. For concurrent maps today, prefer ConcurrentHashMap, which locks finer-grained segments.
Common mistakes
- ✗Storing a
nullkey in aHashtable, then being surprised by theNullPointerException - ✗Using
Hashtablefor thread-safety in new code instead ofConcurrentHashMap - ✗Assuming
Hashtable's per-method locking makes compound get-then-put sequences atomic
Follow-up questions
- →Why does
ConcurrentHashMapscale better under contention than asynchronizedHashtable? - →How does
HashMaprepresent a missing key versus a key mapped to anullvalue?
MiddleTheoryCommonHow does HashMap store entries, resize, and treeify?
How does HashMap store entries, resize, and treeify?
HashMap holds an array of buckets indexed by the key's hash. Colliding keys share a bucket as a linked list. capacity is the bucket-array size and loadFactor (default 0.75) is the fill threshold: when size > capacity * loadFactor the map doubles capacity and rehashes. Since Java 8, a bucket with more than 8 entries (in a large-enough table) converts its list into a red-black tree, cutting worst-case lookup from O(n) to O(log n).
Common mistakes
- ✗Thinking collisions are probed into other buckets rather than chained within one
- ✗Confusing
loadFactor(resize threshold) with a per-bucket capacity limit - ✗Believing treeification turns the whole map into a tree rather than a single bucket
Follow-up questions
- →Why does treeification also require the table to be reasonably large, not just a long bucket?
- →How does a poor
hashCodedefeat theO(1)average and force treeification?
MiddleTheoryCommonWhat is the difference between HashSet and TreeSet in Java?
What is the difference between HashSet and TreeSet in Java?
HashSet stores elements via hashing, giving O(1) average add, remove, and contains, but no ordering — iteration order is unspecified. TreeSet is backed by a red-black tree that keeps elements sorted by their natural Comparable order or a supplied Comparator, costing O(log n) per operation but enabling range queries like first, ceiling, and headSet. Choose HashSet for raw speed, TreeSet when you need sorted order.
Common mistakes
- ✗Expecting
HashSetto iterate in any predictable order, including insertion order - ✗Putting elements lacking a natural order into a
TreeSetwithout supplying aComparator - ✗Forgetting
HashSetrelies onhashCode/equalswhileTreeSetrelies on comparison
Follow-up questions
- →Why does a
TreeSetthrowClassCastExceptionfor elements that aren't mutually comparable? - →When would a
LinkedHashSetbe a better fit than eitherHashSetorTreeSet?
MiddleTheoryCommonWhat is the difference between ArrayList and Vector in Java?
What is the difference between ArrayList and Vector in Java?
Both ArrayList and Vector are resizable arrays with the same O(1) indexed access, but Vector is a legacy class whose methods are synchronized, so every call locks even in single-threaded code, costing performance. ArrayList is unsynchronized and the default choice today. Vector also doubles capacity on growth, while ArrayList grows by about half. For thread-safety, prefer synchronizedList or CopyOnWriteArrayList.
Common mistakes
- ✗Treating
Vector's per-method locking as enough for compound operations like check-then-add - ✗Reaching for
Vectorin new code purely for thread-safety instead of modern concurrent collections - ✗Assuming
VectorandArrayListgrow capacity by the same factor
Follow-up questions
- →Why doesn't
Vector's method-level synchronization make a check-then-act sequence atomic? - →How does
CopyOnWriteArrayListachieve thread-safety differently fromVector?
SeniorPerformanceOccasionalHow do get, add, and contains differ in cost across List, Set, and Map implementations?
How do get, add, and contains differ in cost across List, Set, and Map implementations?
ArrayList gives indexed get in O(1) and amortized O(1) append, but contains scans in O(n). LinkedList is O(1) only at the ends; indexed get and contains are O(n). HashMap and HashSet average O(1) for all three, degrading to O(log n) inside a treeified bucket and to O(n) with a degenerate hashCode. TreeMap and TreeSet are O(log n) for every operation, in exchange for sorted order.
Common mistakes
- ✗Believing
LinkedListindexes inO(1)because it is a linked structure - ✗Quoting
HashMapasO(1)worst-case, ignoring collisions and a badhashCode - ✗Forgetting that
containson anyListis a linear scan, not a hashed lookup
Follow-up questions
- →Why is
ArrayListappend amortizedO(1)rather than plainO(1)? - →When does
LinkedListactually beatArrayListdespite worse cache locality?
SeniorTheoryOccasionalWhat is the difference between fail-fast and fail-safe iterators in Java?
What is the difference between fail-fast and fail-safe iterators in Java?
A fail-fast iterator throws ConcurrentModificationException once it detects the collection was structurally modified mid-iteration — it tracks a modification counter and rechecks it each step. ArrayList and HashMap behave this way. A fail-safe iterator instead works over a copy or snapshot, so concurrent edits don't disrupt it and it never throws — CopyOnWriteArrayList and ConcurrentHashMap do this, at the cost of missing later changes.
Common mistakes
- ✗Removing via the collection's
removeinside a loop instead of the iterator's ownremove - ✗Assuming fail-fast detection is a thread-safety guarantee rather than a best-effort check
- ✗Expecting a fail-safe snapshot iterator to reflect edits made after iteration began
Follow-up questions
- →How does the
modCountfield underpin fail-fast detection, and why is it best-effort? - →What memory and consistency trade-offs does
CopyOnWriteArrayListmake to stay fail-safe?
SeniorTheoryOccasionalWhat is the difference between a shallow copy and a deep copy in Java?
What is the difference between a shallow copy and a deep copy in Java?
A shallow copy duplicates the outer container but shares the same nested element references, so copy and original point at the same inner objects — mutating one's element mutates the other's. A deep copy recursively clones the whole object graph, giving the copy its own independent inner objects, so changes never leak across. clone and copy constructors are shallow by default; a deep copy needs explicit recursive duplication.
Common mistakes
- ✗Assuming a collection's
cloneor copy constructor deep-copies its contained elements - ✗Thinking a deep copy of an immutable element graph differs from a shallow one in practice
- ✗Forgetting cyclic references can make a naive recursive deep copy loop forever
Follow-up questions
- →Why is a shallow copy sufficient when every nested element is immutable?
- →What are the trade-offs of serialization-based deep copy versus hand-written recursive cloning?
MiddleCodeRareStack with push, pop, and getMax all in O(1)
Stack with push, pop, and getMax all in O(1)
Keep two stacks. The main stack holds the values; a second max stack holds, at each level, the maximum of everything pushed so far. On push, also push max(value, maxStack.peek()) onto the max stack. On pop, pop both. getMax returns maxStack.peek(). Because the maximum is recomputed and stored per element, it stays correct after a pop, and all three operations are O(1).
Common mistakes
- ✗Caching a single max value that becomes wrong once it is popped
- ✗Reaching for a sorted structure whose operations are not actually
O(1) - ✗Scanning the elements in
getMax, making itO(n)
Follow-up questions
- →Why does a single cached max value break after a pop, but the paired stack does not?
- →How would you extend this to return the minimum in
O(1)as well?
MiddleCodeRareSliding-window minimum with fast add and getMin
Sliding-window minimum with fast add and getMin
Keep a TreeMap<value,count> plus a FIFO queue holding the window's values in insertion order. On add, if the window is full, poll the oldest from the queue and decrement (or remove) its count in the map; then increment the new value's count and enqueue it. getMin returns map.firstKey(). Each operation is O(log w); a monotonic deque gives amortized O(1).
Common mistakes
- ✗Rescanning the whole window for the minimum on each
getMin, givingO(w) - ✗Using a heap and assuming its head is also the oldest element to evict
- ✗Tracking only a single current min, which breaks when that min is evicted
Follow-up questions
- →Why does a
TreeMapof value to count handle duplicate values correctly? - →How does a monotonic deque reach amortized
O(1)for this problem?