Find Bugs in an LRU Cache
ZipHQBackend Engineer · Technical ScreenLast updated
Identify behavioral bugs in a Java-based LRU Cache implementation using LinkedHashMap, write minimal failing tests, and provide the corrected code.
Problem Statement
A colleague submitted a pull request implementing a fixed-capacity Least Recently Used (LRU) cache in Java using java.util.LinkedHashMap. The code compiles cleanly, but testing reveals that entries are evicted incorrectly under common access patterns.
Review the implementation below, identify all behavioral defects, supply minimal failing test cases for each defect, and write the corrected implementation.
import java.util.LinkedHashMap;
import java.util.Map;
public class SimpleLRUCache {
private final int capacity;
private final Map cache;
public SimpleLRUCache(int capacity) {
if (capacity <= 0) {
throw new IllegalArgumentException("Capacity must be positive");
}
this.capacity = capacity;
// Intended: LRU eviction with initial capacity and default load factor
this.cache = new LinkedHashMap(capacity, 0.75f) {
@Override
protected boolean removeEldestEntry(Map.Entry eldest) {
return size() >= capacity;
}
};
}
public V get(K key) {
return cache.get(key);
}
public void put(K key, V value) {
cache.put(key, value);
}
public int size() {
return cache.size();
}
}
Tasks
- Identify the Bugs: Find the specific defects in the implementation that violate standard LRU cache semantics.
- Minimal Failing Tests: For each bug, describe a short sequence of
putandgetoperations that passes on a correct LRU cache but fails on this code. - Correction: Provide the corrected Java implementation.
- Semantics Explanation: Briefly explain why the Java compiler cannot catch these errors and why behavioral tests are required to validate cache invariants.
Hints
Hint 1
Check the overloaded constructors of LinkedHashMap to see what determines insertion-order versus access-order traversal.
Hint 2
Inspect the exact moment `removeEldestEntry` is evaluated inside `LinkedHashMap.put` and what `size()` evaluates to at that point.
Hint 3
Trace what happens when an existing key is read with `get()` and then a new key is added when the cache is at capacity.
Follow-up questions
- How would you make this LRU cache thread-safe while minimizing lock contention on hot reads?
- How would you implement this cache from scratch without using LinkedHashMap, maintaining O(1) time complexity for get and put?
- If eviction was based on total memory footprint rather than entry count, how would `removeEldestEntry` need to change?
Solution walkthrough is for members
Get the full walkthrough with reference code and complexity analysis, plus Kitty Coach at 300 messages a day. Hints, the code runner and community answers stay free.