Loading...
Loading...
Java interview guide
List, Set, Map, Queue, generics and how the main collections work inside. Every answer is short enough to say aloud, and every question links to the lesson that teaches it.
60 questionsLast updated
String a popular key?The bucket is chosen from the hash code at put time. If the key changes later, its hash code changes and get looks in the wrong bucket, so the entry is lost. String is immutable and has a good equals and hashCode, so it is always safe.
Entries are kept bucket by bucket, and the bucket comes from the hash code, not from insertion time. After a resize, entries move to new buckets, so the order can change. That is why code must never depend on HashMap order.
Lesson: How HashMap works insideHashMap and LinkedHashMap allow one null key. TreeMap does not, because it must compare every key, and comparing with null throws a NullPointerException. All three allow null values.
floorKey and ceilingKey return? Give a use case.floorKey(k) returns the biggest key less than or equal to k, and ceilingKey(k) the smallest key greater than or equal to k; both return null if there is none. I use floorEntry for grade or tax slabs, and ceilingKey to find the next train or the next exam date.
In normal insertion order, no: the key keeps its first position and only the value changes. In access-order mode, yes: the put counts as an access and moves the key to the end.
A cache with a fixed size that, when it is full, removes the Least Recently Used entry, the one that was used longest ago. It keeps the items a user is most likely to ask for again, such as recently viewed products.
Lesson: Build an LRU cache with LinkedHashMapLinkedHashMap do?It is accessOrder. With false (the default) the map keeps insertion order. With true it keeps access order, so a get or put moves the key to the end. Without it, an LRU cache would evict by age of insertion, not by use.
LinkedHashMap, and what is the cost?I use a HashMap from key to node and a doubly linked list of nodes ordered by use. get finds the node in the map and moves it to the front. put adds at the front and, when full, removes the node before the tail and its key from the map. Both operations are O(1).
LRU removes the entry used longest ago. LFU (Least Frequently Used) removes the entry used the fewest times. LRU is simpler and works well for most apps; LFU keeps popular items longer, but needs a counter per entry.
Lesson: Build an LRU cache with LinkedHashMapnew EnumMap<>(Day.class) need the class?The map must know which enum the keys come from, to size its array and to check keys. Generic type information is erased at run time, so the Class object is passed in as a normal argument.
EnumSet?With a factory method, because the class is abstract: of, range(from, to), allOf(Day.class), noneOf(Day.class), complementOf(set) or copyOf(collection). Behind the scenes, the set is stored as bits of a single number.
EnumSet or EnumMap loop?The order in which the constants are declared in the enum, which is the same as their ordinal(). It does not depend on the order in which elements were added.
In a Map<String, Student> with the roll number as the key, filled with put(s.getRoll(), s). Lookup by roll is then O(1) with a HashMap, instead of a loop over a list. If I need roll order, I use a TreeMap.
It overrides equals and hashCode using the same fields, and it is immutable, so its hash code never changes. For a TreeMap it must also be Comparable or used with a comparator. String, Integer and records meet these rules.
The entry stays in the bucket of the old hash code, so get with either the old or the new value fails: the entry is lost until the key is changed back. To rename, I remove the entry with the old key and put its value under a new key object.
A Hostel class with a private Map<String, List<String>> from room to students. Only an allot(room, student) method adds students, and it refuses when the list already has two. A getter returns Collections.unmodifiableMap(rooms), so nobody can bypass the rule.
map.get(5) return null on a Map<Long, String> that has the key 5L?get takes an Object, so 5 is boxed into an Integer. Integer.equals only matches other Integer objects, so it never matches the Long key. I write get(5L).
A map itself is ordered by keys (or not at all), so I copy entrySet() into a List<Map.Entry<K, V>> and sort it with a comparator on getValue(), with a key-based tie-break. If I need a map again, I put the sorted entries into a LinkedHashMap, which keeps that order.
It compiles, but it is risky. If the fields used by hashCode change after put, the entry is stranded in the wrong bucket and lookups fail. I use immutable keys like String, Integer or records, and never change a key while it is in a map.
map.forEach?It throws ConcurrentModificationException, just like a for-each loop, because the map's structure changed while it was being walked. I use map.values().removeIf(...) or map.entrySet().removeIf(...) instead.
offer/poll/peek and add/remove/element?They do the same jobs but fail differently. add, remove and element throw an exception, for example NoSuchElementException on an empty queue. offer returns false, and poll and peek return null, so I can handle the case with an if.
Because poll and peek return null to say the queue is empty. If null could be stored, a null result would be ambiguous. So offer(null) throws NullPointerException instead.
Every job gets a short, fixed turn in order; an unfinished job goes to the back and waits for its next turn. In code I poll a job, run it for one time slice, and offer it back if work remains. Short jobs finish early and no job waits forever.
A stack is a LIFO collection: the last value pushed is the first one popped. It is used for undo, the Back button, checking brackets, evaluating expressions and, inside the JVM, for method calls.
Lesson: Stacks with ArrayDequejava.util.Stack not recommended?It is a legacy class that extends Vector. It exposes list methods like get and add(index, x), so code can break LIFO, and every method takes a lock that a normal program does not need. The Java documentation itself recommends Deque implementations such as ArrayDeque.
I scan the string; each opening bracket is pushed. For a closing bracket, I fail if the stack is empty, otherwise I pop and check that it is the matching opener. At the end the string is balanced only if the stack is empty. It is O(n) time.
Lesson: Stacks with ArrayDequemin() on a stack be O(1)?I keep a second stack of minimums. On push, I also push the value there if it is less than or equal to the current minimum; on pop, if the popped value equals the top of the min stack, I pop that too. The current minimum is always the top of the min stack.
Lesson: Stacks with ArrayDequeAs a queue, offer is offerLast, poll is pollFirst and peek is peekFirst. As a stack, push is addFirst, pop is removeFirst and peek is peekFirst. So both a queue and a stack take values from the front.
ArrayDeque keeps values in one array used as a circle, so it has no node objects and is friendly to memory. LinkedList creates a node with two links for each value. Both have O(1) end operations, but ArrayDeque does less work and uses less memory.
I keep a deque of indexes whose values are decreasing. For each new index, I drop the front if it is outside the window, pop smaller values from the back, then add the index; the front is the window's maximum. Every index is added and removed at most once, so it is O(n), compared with O(n × k) for checking every window.
Lesson: Deque: both endsA normal queue is FIFO: poll returns the oldest value. A PriorityQueue returns the value with the highest priority, which by default is the smallest by natural order, or whatever a comparator says. Arrival order does not matter.
toString and the iterator walk the heap's array, which is only partly ordered: parents are smaller than children, but siblings and cousins are in no particular order. To get sorted order, I poll repeatedly or copy into a list and sort.
No. Two elements that compare as equal can come out in either order. If first-come-first-served matters among equals, I add a tie-breaker, such as an arrival counter, with thenComparingInt.
The heap is not told, so its order becomes wrong and poll may return the wrong element. The safe way is to remove the element, change it, and offer it again, or to use immutable elements such as records.
BFS starts from one node, puts it in a queue, and repeatedly polls a node and offers its unvisited neighbours. Because the queue is FIFO, all nodes one step away are handled before nodes two steps away, so the first time a node is reached it is by the fewest steps. This holds when every step costs the same.
Lesson: Common mistakes and practiceIt is a stack whose values stay in increasing or decreasing order, because I pop values that break the order before pushing a new one. It answers "next greater" or "next smaller" questions, such as how many days until a higher price, in O(n) instead of O(n²).
Lesson: Common mistakes and practicepush and pop work at the front, while offer adds at the back. Mixing them puts values in an order that is hard to reason about. If both ends are really needed, I use offerFirst, offerLast, pollFirst and pollLast, so every line names its end.
Collections have a private constructor?It only holds static helper methods and no state, so an object of it would be useless. The private constructor stops anyone from creating one. Math and Arrays follow the same pattern.
Not fully. You cannot add, remove or replace items through it, but the original list behind a view can still change. Also, the objects inside are not frozen: if it holds Student objects with setters, those students can still be changed.
Collections.max do on an empty list, and on a tie?On an empty collection it throws NoSuchElementException, so I check isEmpty() first. On a tie it returns the first of the equal items it met, because it only replaces its best item when it finds a strictly larger one.
Collections.emptyList() instead of null?Almost always, when a method finds nothing. Callers can loop over an empty list or check isEmpty() without a null check, so there is no NullPointerException. It is also shared and read-only, so no new object is created each time.
List.of and Collections.unmodifiableList?List.of builds a new fixed list that nobody can change. unmodifiableList is only a read-only view on another list: readers cannot change it, but they see every later change that the owner makes. For a snapshot use List.copyOf.
Set.of throw on a repeated item, and why should you not rely on its order?Set.of treats a repeat as a likely mistake and throws IllegalArgumentException. Its iteration order is deliberately unspecified and can differ between runs, so never depend on it. Copy into a TreeSet for a sorted order.
It is a copy a class makes so that outsiders cannot change its data. Copy what comes in with List.copyOf(items) in the constructor, keep it in a final field, and return that read-only list from the getter.
Arrays.asList return, and why can't you add to it?It returns a fixed-size list backed by the array you passed. The list has no storage of its own, so it cannot grow or shrink: add and remove throw UnsupportedOperationException. set works, and it writes into the array.
Arrays.asList(new int[]{1, 2, 3}).size() return 1?Generics only work with objects, so an int cannot be a list item. Java treats the whole int[] as one object and makes a List<int[]> with one item. I use a loop to build a List<Integer>, or start with an Integer[].
Arrays.sort and Collections.sort?Arrays.sort sorts arrays, including primitive arrays, and can sort a range. Collections.sort sorts a List, and is the same as list.sort(...). For objects both use a stable sort, so equal items keep their order.
Collections.sort on a list from Arrays.asList, does the array change?Yes. Sorting does not change the size, so the fixed-size view allows it. The view has no storage of its own, so the new order is written into the array, and the original array ends up sorted too.
Lesson: Moving between arrays and collectionsequals but a hashCode that returns a random value. Which works better, map.containsKey(key) or map.containsValue(value)?containsValue still works, because it scans the values and uses only equals. containsKey fails, because it first uses hashCode to pick a bucket and the random value sends the lookup to the wrong one. The same bug breaks get, remove and HashSet.contains, while ArrayList.contains is unaffected.
HashMap?If the field is used in hashCode, the entry is now in the wrong bucket. get, containsKey and remove miss it, but it still counts in size() and shows up when you loop. That is why keys should be immutable; otherwise remove the key, change it, and put it back.
TreeSet drop an element that is not equal to any other element?Yes. A TreeSet treats compare(a, b) == 0 as a duplicate, even if a.equals(b) is false. For example, a comparator on marks only drops the second student with the same marks. I add tie-breakers with thenComparing so the order is consistent with equals.
HashMap key?It compiles, but arrays do not override equals and hashCode, so lookups only work with the very same array object. A new array with the same numbers is not found. I use List.of(...) or a record as the key instead.
ArrayList vs LinkedList: when would you actually pick LinkedList?Almost never. ArrayList has O(1) index access, uses less memory and is friendly to the CPU cache. LinkedList is only quick at its ends and when you already have an iterator at the right place, and for ends ArrayDeque is usually faster. I would pick LinkedList only if I measured that it helps.
HashSet, LinkedHashSet or TreeSet: what is the difference?All three reject duplicates. HashSet is the fastest and has no order. LinkedHashSet is almost as fast and keeps insertion order. TreeSet is O(log n), keeps items sorted, uses compareTo instead of equals, and offers first, ceiling and headSet.
I would time each with System.nanoTime() before and after, run it several times and keep the best or the average after a warm-up, and use data big enough to take milliseconds. I would not print inside the timed part. For serious results I would use a benchmark tool like JMH, and I trust the ratio more than the exact numbers.
ArrayList.add?Most adds just write into the next free slot, which is O(1). When the array is full, ArrayList copies everything into a bigger array, about one and a half times the size, which is O(n). Because the size grows by a factor each time, those copies are rare, and averaged over all adds the cost per add stays constant.
It checks the collection's modification counter on every next() and throws ConcurrentModificationException if the collection changed behind it. To remove safely, I use Iterator.remove(), removeIf, entrySet().removeIf for maps, or loop over a copy.
put on a HashMap.It calls hashCode() on the key, spreads the bits and picks a bucket index. If the bucket has an entry whose key is equals to mine, it replaces the value and returns the old one. Otherwise it adds a new entry, returns null, and resizes to twice the buckets if the map is now more than 75% full.
HashMap allow a null key but TreeMap does not?HashMap treats a null key as a special case and stores it in bucket 0. TreeMap must compare every key with compareTo or a Comparator, and calling a method on null throws NullPointerException.
I would extend LinkedHashMap with access order turned on, so every get moves an entry to the end. Then I override removeEldestEntry to return true when size() is above the capacity, which drops the least recently used entry after each put. Both lookup and update stay O(1).
Each lesson has quizzes, practice and more interview questions on the same topic.
Open the first lesson