javaadvanced
LRU Cache — Thread-Safe Implementation
Implement a thread-safe LRU cache with configurable size, TTL expiry, and statistics tracking.
javaPress ⌘/Ctrl + Shift + C to copy
import java.util.*;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.time.Instant;
public class LRUCache<K, V> {
private final int maxSize;
private final long ttlMillis;
private final LinkedHashMap<K, CacheEntry<V>> map;
private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
private long hits = 0, misses = 0;
record CacheEntry<V>(V value, Instant expiresAt) {
boolean isExpired() { return Instant.now().isAfter(expiresAt); }
}
public LRUCache(int maxSize, long ttlMillis) {
this.maxSize = maxSize;
this.ttlMillis = ttlMillis;
this.map = new LinkedHashMap<>(maxSize, 0.75f, true) {
@Override
protected boolean removeEldestEntry(Map.Entry<K, CacheEntry<V>> eldest) {
return size() > maxSize || eldest.getValue().isExpired();
}
};
}
public V get(K key) {
lock.readLock().lock();
try {
CacheEntry<V> entry = map.get(key);
if (entry == null || entry.isExpired()) {
misses++;
if (entry != null) map.remove(key);
return null;
}
hits++;
return entry.value();
} finally {
lock.readLock().unlock();
}
}
public void put(K key, V value) {
lock.writeLock().lock();
try {
map.put(key, new CacheEntry<>(value,
Instant.now().plusMillis(ttlMillis)));
} finally {
lock.writeLock().unlock();
}
}
public V getOrCompute(K key, java.util.function.Function<K, V> compute) {
V cached = get(key);
if (cached != null) return cached;
lock.writeLock().lock();
try {
// Double-check after acquiring write lock
CacheEntry<V> entry = map.get(key);
if (entry != null && !entry.isExpired()) return entry.value();
V value = compute.apply(key);
put(key, value);
return value;
} finally {
lock.writeLock().unlock();
}
}
public void evict(K key) {
lock.writeLock().lock();
try { map.remove(key); }
finally { lock.writeLock().unlock(); }
}
public int size() { return map.size(); }
public String stats() {
long total = hits + misses;
double rate = total == 0 ? 0 : (double) hits / total * 100;
return String.format("hits=%d misses=%d rate=%.1f%% size=%d", hits, misses, rate, size());
}
public static void main(String[] args) throws Exception {
LRUCache<String, String> cache = new LRUCache<>(3, 5000);
cache.put("a", "Apple");
cache.put("b", "Banana");
cache.put("c", "Cherry");
cache.get("a"); // access "a" (now most recent)
cache.put("d", "Date"); // evicts "b" (least recently used)
System.out.println(cache.get("b")); // null
System.out.println(cache.get("a")); // Apple
System.out.println(cache.stats());
}
}Use Cases
- Application-level caching without external systems
- Reducing expensive computation with memoization
- API response caching with TTL expiry
Tags
Related Snippets
Similar patterns you can reuse in the same workflow.
javabeginner
HashMap Operations and Patterns
Essential HashMap operations: put, get, merge, compute, getOrDefault, and iteration patterns.
Best for: Counting word frequencies in text
#java#collections
javaintermediate
Java Map — Advanced Operations
Master Map operations: compute, merge, getOrDefault, multi-map, BiMap, and stream-based grouping.
Best for: Aggregation and grouping operations on data
#java#map
javaintermediate
Tree Traversal — DFS and BFS
Implement tree data structure with DFS (pre/in/post order), BFS, and recursive/iterative traversals.
Best for: Tree-based data structure traversal
#java#tree
javaadvanced
Sorting Algorithms — Quick, Merge, and Heap
Implement classic sorting algorithms in Java: quicksort, mergesort, heapsort, and their time complexity.
Best for: Understanding sorting algorithm trade-offs
#java#sorting