mirror of
https://github.com/TheAlgorithms/Java.git
synced 2025-07-05 16:27:33 +08:00
Enhance docs, add more tests in LFUCache
(#5949)
This commit is contained in:
@ -6,16 +6,21 @@ import java.util.Map;
|
||||
/**
|
||||
* The {@code LFUCache} class implements a Least Frequently Used (LFU) cache.
|
||||
* An LFU cache evicts the least frequently used item when the cache reaches its capacity.
|
||||
* It keeps track of how many times each item is used and maintains a doubly linked list
|
||||
* for efficient addition and removal of items based on their frequency of use.
|
||||
* It maintains a mapping of keys to nodes, where each node contains the key, its associated value,
|
||||
* and a frequency count that tracks how many times the item has been accessed. A doubly linked list
|
||||
* is used to efficiently manage the ordering of items based on their usage frequency.
|
||||
*
|
||||
* @param <K> The type of keys maintained by this cache.
|
||||
* @param <V> The type of mapped values.
|
||||
* <p>This implementation is designed to provide O(1) time complexity for both the {@code get} and
|
||||
* {@code put} operations, which is achieved through the use of a hashmap for quick access and a
|
||||
* doubly linked list for maintaining the order of item frequencies.</p>
|
||||
*
|
||||
* <p>
|
||||
* Reference: <a href="https://en.wikipedia.org/wiki/Least_frequently_used">LFU Cache - Wikipedia</a>
|
||||
* </p>
|
||||
*
|
||||
* @param <K> The type of keys maintained by this cache.
|
||||
* @param <V> The type of mapped values.
|
||||
*
|
||||
* @author Akshay Dubey (https://github.com/itsAkshayDubey)
|
||||
*/
|
||||
public class LFUCache<K, V> {
|
||||
@ -75,7 +80,7 @@ public class LFUCache<K, V> {
|
||||
|
||||
/**
|
||||
* Retrieves the value associated with the given key from the cache.
|
||||
* If the key exists, the node's frequency is increased and the node is repositioned
|
||||
* If the key exists, the node's frequency is incremented, and the node is repositioned
|
||||
* in the linked list based on its updated frequency.
|
||||
*
|
||||
* @param key The key whose associated value is to be returned.
|
||||
|
@ -1,6 +1,8 @@
|
||||
package com.thealgorithms.datastructures.caches;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
@ -22,7 +24,7 @@ class LFUCacheTest {
|
||||
lfuCache.put(6, 60);
|
||||
|
||||
// will return null as value with key 2 is now evicted
|
||||
assertEquals(null, lfuCache.get(2));
|
||||
assertNull(lfuCache.get(2));
|
||||
|
||||
// should return 60
|
||||
assertEquals(60, lfuCache.get(6));
|
||||
@ -30,7 +32,7 @@ class LFUCacheTest {
|
||||
// this operation will remove value with key as 3
|
||||
lfuCache.put(7, 70);
|
||||
|
||||
assertEquals(null, lfuCache.get(2));
|
||||
assertNull(lfuCache.get(2));
|
||||
assertEquals(70, lfuCache.get(7));
|
||||
}
|
||||
|
||||
@ -41,7 +43,7 @@ class LFUCacheTest {
|
||||
lfuCache.put(2, "Beta");
|
||||
lfuCache.put(3, "Gamma");
|
||||
lfuCache.put(4, "Delta");
|
||||
lfuCache.put(5, "Eplison");
|
||||
lfuCache.put(5, "Epsilon");
|
||||
|
||||
// get method call will increase frequency of key 1 by 1
|
||||
assertEquals("Alpha", lfuCache.get(1));
|
||||
@ -50,7 +52,7 @@ class LFUCacheTest {
|
||||
lfuCache.put(6, "Digamma");
|
||||
|
||||
// will return null as value with key 2 is now evicted
|
||||
assertEquals(null, lfuCache.get(2));
|
||||
assertNull(lfuCache.get(2));
|
||||
|
||||
// should return string Digamma
|
||||
assertEquals("Digamma", lfuCache.get(6));
|
||||
@ -58,25 +60,79 @@ class LFUCacheTest {
|
||||
// this operation will remove value with key as 3
|
||||
lfuCache.put(7, "Zeta");
|
||||
|
||||
assertEquals(null, lfuCache.get(2));
|
||||
assertNull(lfuCache.get(2));
|
||||
assertEquals("Zeta", lfuCache.get(7));
|
||||
}
|
||||
|
||||
/**
|
||||
* test addNodeWithUpdatedFrequency method
|
||||
* @author yuluo
|
||||
*/
|
||||
@Test
|
||||
void testAddNodeWithUpdatedFrequency() {
|
||||
void testUpdateValueShouldPreserveFrequency() {
|
||||
LFUCache<Integer, String> lfuCache = new LFUCache<>(3);
|
||||
lfuCache.put(1, "beijing");
|
||||
lfuCache.put(2, "shanghai");
|
||||
lfuCache.put(3, "gansu");
|
||||
lfuCache.put(1, "A");
|
||||
lfuCache.put(2, "B");
|
||||
lfuCache.put(3, "C");
|
||||
|
||||
assertEquals("beijing", lfuCache.get(1));
|
||||
assertEquals("A", lfuCache.get(1)); // Accessing key 1
|
||||
lfuCache.put(4, "D"); // This should evict key 2
|
||||
|
||||
lfuCache.put(1, "shanxi");
|
||||
assertNull(lfuCache.get(2)); // Key 2 should be evicted
|
||||
assertEquals("C", lfuCache.get(3)); // Key 3 should still exist
|
||||
assertEquals("A", lfuCache.get(1)); // Key 1 should still exist
|
||||
|
||||
assertEquals("shanxi", lfuCache.get(1));
|
||||
lfuCache.put(1, "Updated A"); // Update the value of key 1
|
||||
assertEquals("Updated A", lfuCache.get(1)); // Check if the update was successful
|
||||
}
|
||||
|
||||
@Test
|
||||
void testEvictionPolicyWhenFull() {
|
||||
LFUCache<Integer, String> lfuCache = new LFUCache<>(2);
|
||||
lfuCache.put(1, "One");
|
||||
lfuCache.put(2, "Two");
|
||||
|
||||
assertEquals("One", lfuCache.get(1)); // Access key 1
|
||||
lfuCache.put(3, "Three"); // This should evict key 2 (least frequently used)
|
||||
|
||||
assertNull(lfuCache.get(2)); // Key 2 should be evicted
|
||||
assertEquals("One", lfuCache.get(1)); // Key 1 should still exist
|
||||
assertEquals("Three", lfuCache.get(3)); // Check if key 3 exists
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetFromEmptyCacheShouldReturnNull() {
|
||||
LFUCache<Integer, String> lfuCache = new LFUCache<>(3);
|
||||
assertNull(lfuCache.get(1)); // Should return null as the cache is empty
|
||||
}
|
||||
|
||||
@Test
|
||||
void testPutNullValueShouldStoreNull() {
|
||||
LFUCache<Integer, String> lfuCache = new LFUCache<>(3);
|
||||
lfuCache.put(1, null); // Store a null value
|
||||
|
||||
assertNull(lfuCache.get(1)); // Should return null
|
||||
}
|
||||
|
||||
@Test
|
||||
void testInvalidCacheCapacityShouldThrowException() {
|
||||
assertThrows(IllegalArgumentException.class, () -> new LFUCache<>(0));
|
||||
assertThrows(IllegalArgumentException.class, () -> new LFUCache<>(-1));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testMultipleAccessPatterns() {
|
||||
LFUCache<Integer, String> lfuCache = new LFUCache<>(5);
|
||||
lfuCache.put(1, "A");
|
||||
lfuCache.put(2, "B");
|
||||
lfuCache.put(3, "C");
|
||||
lfuCache.put(4, "D");
|
||||
|
||||
assertEquals("A", lfuCache.get(1)); // Access 1
|
||||
lfuCache.put(5, "E"); // Should not evict anything yet
|
||||
lfuCache.put(6, "F"); // Evict B
|
||||
|
||||
assertNull(lfuCache.get(2)); // B should be evicted
|
||||
assertEquals("C", lfuCache.get(3)); // C should still exist
|
||||
assertEquals("D", lfuCache.get(4)); // D should still exist
|
||||
assertEquals("A", lfuCache.get(1)); // A should still exist
|
||||
assertEquals("E", lfuCache.get(5)); // E should exist
|
||||
assertEquals("F", lfuCache.get(6)); // F should exist
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user