Enhance docs, add more tests in LRUCache (#5950)

This commit is contained in:
Hardik Pawar
2024-10-23 23:46:19 +05:30
committed by GitHub
parent 520e46443e
commit b64e53cd3d
2 changed files with 194 additions and 39 deletions

View File

@ -4,15 +4,40 @@ import java.util.HashMap;
import java.util.Map;
/**
* Least recently used (LRU)
* <p>
* Discards the least recently used items first. This algorithm requires keeping
* track of what was used when, which is expensive if one wants to make sure the
* algorithm always discards the least recently used item.
* https://en.wikipedia.org/wiki/Cache_replacement_policies#Least_recently_used_(LRU)
* A Least Recently Used (LRU) Cache implementation.
*
* @param <K> key type
* @param <V> value type
* <p>An LRU cache is a fixed-size cache that maintains items in order of use. When the cache reaches
* its capacity and a new item needs to be added, it removes the least recently used item first.
* This implementation provides O(1) time complexity for both get and put operations.</p>
*
* <p>Features:</p>
* <ul>
* <li>Fixed-size cache with configurable capacity</li>
* <li>Constant time O(1) operations for get and put</li>
* <li>Thread-unsafe - should be externally synchronized if used in concurrent environments</li>
* <li>Supports null values but not null keys</li>
* </ul>
*
* <p>Implementation Details:</p>
* <ul>
* <li>Uses a HashMap for O(1) key-value lookups</li>
* <li>Maintains a doubly-linked list for tracking access order</li>
* <li>The head of the list contains the least recently used item</li>
* <li>The tail of the list contains the most recently used item</li>
* </ul>
*
* <p>Example usage:</p>
* <pre>
* LRUCache<String, Integer> cache = new LRUCache<>(3); // Create cache with capacity 3
* cache.put("A", 1); // Cache: A=1
* cache.put("B", 2); // Cache: A=1, B=2
* cache.put("C", 3); // Cache: A=1, B=2, C=3
* cache.get("A"); // Cache: B=2, C=3, A=1 (A moved to end)
* cache.put("D", 4); // Cache: C=3, A=1, D=4 (B evicted)
* </pre>
*
* @param <K> the type of keys maintained by this cache
* @param <V> the type of mapped values
*/
public class LRUCache<K, V> {
@ -30,6 +55,11 @@ public class LRUCache<K, V> {
setCapacity(cap);
}
/**
* Returns the current capacity of the cache.
*
* @param newCapacity the new capacity of the cache
*/
private void setCapacity(int newCapacity) {
checkCapacity(newCapacity);
for (int i = data.size(); i > newCapacity; i--) {
@ -39,6 +69,11 @@ public class LRUCache<K, V> {
this.cap = newCapacity;
}
/**
* Evicts the least recently used item from the cache.
*
* @return the evicted entry
*/
private Entry<K, V> evict() {
if (head == null) {
throw new RuntimeException("cache cannot be empty!");
@ -50,12 +85,25 @@ public class LRUCache<K, V> {
return evicted;
}
/**
* Checks if the capacity is valid.
*
* @param capacity the capacity to check
*/
private void checkCapacity(int capacity) {
if (capacity <= 0) {
throw new RuntimeException("capacity must greater than 0!");
}
}
/**
* Returns the value to which the specified key is mapped, or null if this cache contains no
* mapping for the key.
*
* @param key the key whose associated value is to be returned
* @return the value to which the specified key is mapped, or null if this cache contains no
* mapping for the key
*/
public V get(K key) {
if (!data.containsKey(key)) {
return null;
@ -65,6 +113,11 @@ public class LRUCache<K, V> {
return entry.getValue();
}
/**
* Moves the specified entry to the end of the list.
*
* @param entry the entry to move
*/
private void moveNodeToLast(Entry<K, V> entry) {
if (tail == entry) {
return;
@ -86,6 +139,12 @@ public class LRUCache<K, V> {
tail = entry;
}
/**
* Associates the specified value with the specified key in this cache.
*
* @param key the key with which the specified value is to be associated
* @param value the value to be associated with the specified key
*/
public void put(K key, V value) {
if (data.containsKey(key)) {
final Entry<K, V> existingEntry = data.get(key);
@ -107,6 +166,11 @@ public class LRUCache<K, V> {
data.put(key, newEntry);
}
/**
* Adds a new entry to the end of the list.
*
* @param newEntry the entry to add
*/
private void addNewEntry(Entry<K, V> newEntry) {
if (data.isEmpty()) {
head = newEntry;