LinkedHashMap maintains a doubly-linked list across its entries, preserving insertion order (or access order, depending on configuration). Its protected removeEldestEntry() method lets you control automatic eviction — making it the simplest way to build a bounded cache in Java without any third-party library.
What Is removeEldestEntry()?
removeEldestEntry(Map.Entry<K,V> eldest) is a protected method called automatically after every put() or putAll(). If it returns true, LinkedHashMap removes the eldest (first) entry from the map. If it returns false, nothing is removed.
The default implementation always returns false — no automatic removal. You override it to add eviction logic.
Basic Example: Size-Bounded Map
Keep at most 4 entries — automatically evict the oldest when a 5th is added:
import java.util.*;
LinkedHashMap<Integer, String> boundedMap = new LinkedHashMap<>() {
@Override
protected boolean removeEldestEntry(Map.Entry<Integer, String> eldest) {
return size() > 4;
}
};
boundedMap.put(0, "zero");
boundedMap.put(1, "one");
boundedMap.put(2, "two");
boundedMap.put(3, "three");
System.out.println("After 4 puts: " + boundedMap);
// {0=zero, 1=one, 2=two, 3=three}
boundedMap.put(4, "four"); // triggers eviction of key=0
System.out.println("After 5th put: " + boundedMap);
// {1=one, 2=two, 3=three, 4=four}
When the 5th entry is added, size() becomes 5, removeEldestEntry() returns true, and the entry with key 0 (the eldest) is automatically removed.
LRU Cache Using Access Order
For a true Least Recently Used (LRU) cache, construct LinkedHashMap with accessOrder = true. This moves accessed entries to the tail of the list, so the head always holds the least-recently-used entry:
int capacity = 3;
LinkedHashMap<String, String> lruCache = new LinkedHashMap<>(capacity, 0.75f, true) {
@Override
protected boolean removeEldestEntry(Map.Entry<String, String> eldest) {
return size() > capacity;
}
};
lruCache.put("a", "Apple");
lruCache.put("b", "Banana");
lruCache.put("c", "Cherry");
System.out.println(lruCache); // {a=Apple, b=Banana, c=Cherry}
lruCache.get("a"); // "a" accessed — moves to tail (most recent)
System.out.println(lruCache); // {b=Banana, c=Cherry, a=Apple}
lruCache.put("d", "Date"); // evicts "b" — least recently used
System.out.println(lruCache); // {c=Cherry, a=Apple, d=Date}
The LinkedHashMap(initialCapacity, loadFactor, accessOrder) constructor's third argument enables access-order mode. With accessOrder = true, every get() reorders the entry to the tail, so the head is always the LRU candidate for eviction.
Insertion Order vs Access Order
| Mode | How It Works | What Gets Evicted |
|---|---|---|
| Insertion order (default) | Entries stay in the order they were put() | The entry that was inserted first |
Access order (true) | Accessed entries move to the tail | The entry least recently accessed or inserted |
Reusable LRU Cache Class
import java.util.LinkedHashMap;
import java.util.Map;
public class LruCache<K, V> extends LinkedHashMap<K, V> {
private final int maxSize;
public LruCache(int maxSize) {
super(maxSize, 0.75f, true); // accessOrder = true
this.maxSize = maxSize;
}
@Override
protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
return size() > maxSize;
}
}
// Usage:
LruCache<Integer, String> cache = new LruCache<>(5);
cache.put(1, "Page 1");
cache.put(2, "Page 2");
cache.get(1); // "1" is now most recently used
System.out.println(cache.containsKey(1)); // true
Thread Safety
LinkedHashMap is not thread-safe. For a concurrent LRU cache, wrap it with Collections.synchronizedMap():
Map<String, String> syncCache = Collections.synchronizedMap(
new LruCache<>(100)
);
For high-concurrency workloads, consider ConcurrentHashMap with a manual size-bounding strategy, or a library like Caffeine which provides a lock-free LRU cache.
Summary
Override removeEldestEntry() in a LinkedHashMap subclass to automatically evict entries when the map exceeds a size limit. For a true LRU cache, construct LinkedHashMap with accessOrder = true so accesses move entries to the tail and the head always holds the least-recently-used entry. This pattern is clean, built into the JDK, and requires no external dependencies.
No comments :
Post a Comment
Please leave your message queries or suggetions.
Note: Only a member of this blog may post a comment.