Collections Interview Questions
The Java Collections Framework is one of the most tested interview topics. Know implementation internals, time complexity, and when to use each collection.
Collections Hierarchy
Collection
├── List (ordered, allows duplicates)
│ ├── ArrayList
│ ├── LinkedList
│ └── Vector (legacy, synchronized)
├── Set (no duplicates)
│ ├── HashSet
│ ├── LinkedHashSet
│ └── TreeSet (sorted)
└── Queue
├── PriorityQueue
└── Deque (ArrayDeque, LinkedList)
Map (key-value, not a Collection)
├── HashMap
├── LinkedHashMap
├── TreeMap (sorted)
├── Hashtable (legacy, synchronized)
└── ConcurrentHashMap
ArrayList vs LinkedList
Q: When to use ArrayList vs LinkedList?
| Operation | ArrayList | LinkedList |
|---|---|---|
| get(index) | O(1) | O(n) |
| add(end) | O(1)* | O(1) |
| add(middle) | O(n) | O(n) — must traverse |
| remove(middle) | O(n) | O(n) — must traverse |
| Memory | Less overhead | More (node pointers) |
*Amortized — occasional resize copies array.
Default choice: ArrayList — cache-friendly contiguous memory, faster iteration. LinkedList rarely wins in practice (despite O(1) insert “in the middle”, finding the position is O(n)).
List<String> list = new ArrayList<>(); // default choice
list.add("a");
list.get(0); // O(1)
HashMap Internals
Q: How does HashMap work internally?
- Computes
hash = key.hashCode() - Bucket index =
(n - 1) & hash(n = array length, power of 2) - Stores
Node(key, value, hash, next) in bucket - Collision: linked list (Java 7) or tree (Java 8+, when bucket > 8 nodes)
- Load factor 0.75 — resize doubles capacity when 75% full
Map<String, Integer> map = new HashMap<>();
map.put("apple", 3);
map.get("apple"); // O(1) average
Q: Why is HashMap not thread-safe?
Concurrent modification can cause infinite loops (Java 7) or lost updates. Use:
ConcurrentHashMap— thread-safe, segmented lockingCollections.synchronizedMap()— wraps with synchronized methods (slower)- External synchronization
HashMap vs Hashtable vs ConcurrentHashMap
| HashMap | Hashtable | ConcurrentHashMap | |
|---|---|---|---|
| Null keys/values | One null key, null values | Not allowed | Not allowed |
| Thread-safe | No | Yes (synchronized) | Yes (lock striping) |
| Performance | Fastest (single thread) | Slow (global lock) | Fast (concurrent) |
| Legacy | No | Yes (avoid) | No |
// Thread-safe concurrent access
ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();
map.put("count", 1);
map.merge("count", 1, Integer::sum); // atomic increment
HashSet Internals
Q: How is HashSet implemented?
HashSet is a HashMap where elements are keys and values are dummy PRESENT objects:
// Conceptually
private transient HashMap<E, Object> map;
private static final Object PRESENT = new Object();
public boolean add(E e) {
return map.put(e, PRESENT) == null;
}
Therefore: add/contains/remove are O(1) average, requires good hashCode().
TreeMap and TreeSet
Q: When to use TreeMap/TreeSet?
Red-black tree implementation — O(log n) operations, elements sorted by natural order or Comparator:
TreeMap<Integer, String> map = new TreeMap<>();
map.put(3, "three");
map.put(1, "one");
map.firstKey(); // 1
map.lastKey(); // 3
map.headMap(3); // entries with key < 3
Use when you need sorted iteration or range queries.
Fail-Fast vs Fail-Safe Iterators
Q: What is fail-fast behavior?
List<String> list = new ArrayList<>(List.of("a", "b", "c"));
for (String s : list) {
list.remove(s); // ConcurrentModificationException!
}
Iterator tracks modCount — detects structural modification during iteration.
Safe removal:
Iterator<String> it = list.iterator();
while (it.hasNext()) {
if (it.next().equals("b")) {
it.remove(); // safe
}
}
// Or Java 8+
list.removeIf(s -> s.equals("b"));
Fail-safe iterators (ConcurrentHashMap, CopyOnWriteArrayList) iterate over a snapshot — no exception but may not see recent changes.
Comparable vs Comparator
// Comparable — natural ordering (inside class)
public record User(String name, int age) implements Comparable<User> {
@Override
public int compareTo(User other) {
return Integer.compare(this.age, other.age);
}
}
// Comparator — external, multiple sort orders
Comparator<User> byName = Comparator.comparing(User::name);
Comparator<User> byAgeDesc = Comparator.comparingInt(User::age).reversed();
users.sort(byName);
Common Interview Questions
Q: How to convert List to Set and remove duplicates?
List<String> list = List.of("a", "b", "a", "c");
Set<String> set = new LinkedHashSet<>(list); // preserves order
List<String> unique = new ArrayList<>(set);
Q: Find the first non-repeating character.
char firstUnique(String s) {
Map<Character, Integer> count = new LinkedHashMap<>();
for (char c : s.toCharArray()) {
count.merge(c, 1, Integer::sum);
}
return count.entrySet().stream()
.filter(e -> e.getValue() == 1)
.map(Map.Entry::getKey)
.findFirst()
.orElse('\0');
}
Q: Implement LRU Cache.
class LRUCache<K, V> extends LinkedHashMap<K, V> {
private final int capacity;
LRUCache(int capacity) {
super(capacity, 0.75f, true); // accessOrder = true
this.capacity = capacity;
}
@Override
protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
return size() > capacity;
}
}
Or use Caffeine cache library in production.
Stream API Quick Reference
List<Integer> result = numbers.stream()
.filter(n -> n > 0)
.map(n -> n * 2)
.sorted()
.distinct()
.collect(Collectors.toList());
Map<String, Long> counts = words.stream()
.collect(Collectors.groupingBy(w -> w, Collectors.counting()));
| Operation | Parallel safe? |
|---|---|
| filter, map | Yes (stateless) |
| sorted, distinct | Yes (with merge cost) |
| forEach | Side effects need care |
Key Takeaways
- Default to
ArrayList,HashMap,HashSet - Know O(1) vs O(log n) vs O(n) for each operation
- Never modify a collection while iterating (except iterator.remove())
- Use
ConcurrentHashMapfor concurrent access, not synchronized wrappers - Override
equals()andhashCode()together for hash-based collections