Merge pull request #1371 from tanmaylaud/Development
Added LFU Cache implementation
This commit is contained in:
commit
80b765ba30
155
src/main/java/com/caching/LFUCache.java
Normal file
155
src/main/java/com/caching/LFUCache.java
Normal file
@ -0,0 +1,155 @@
|
|||||||
|
package com.caching;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.NoSuchElementException;
|
||||||
|
import java.util.TreeMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Your LFUCache object can be instantiated and called as such: LFUCache
|
||||||
|
* lfuCache = new LFUCache(capacity); lfuCache.put(key,value); int param_1 =
|
||||||
|
* lfuCache.get(key);
|
||||||
|
*/
|
||||||
|
class LFUCache<T> {
|
||||||
|
// internal Node to store cache element
|
||||||
|
private class Node {
|
||||||
|
int key;
|
||||||
|
T value;
|
||||||
|
int freq;
|
||||||
|
Node next;
|
||||||
|
Node pre;
|
||||||
|
|
||||||
|
public Node(int key, T value, int freq) {
|
||||||
|
this.key = key;
|
||||||
|
this.value = value;
|
||||||
|
this.freq = freq;
|
||||||
|
next = pre = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toString() {
|
||||||
|
return " Key: " + key + "Value: " + value + "Freq: " + freq;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// internal Doubly Linked List to store cache nodes
|
||||||
|
private class DLL {
|
||||||
|
Node head;
|
||||||
|
Node tail;
|
||||||
|
int len;
|
||||||
|
|
||||||
|
public DLL() {
|
||||||
|
head = new Node(-1, null, -1);
|
||||||
|
tail = new Node(-1, null, -1);
|
||||||
|
head.next = tail;
|
||||||
|
tail.pre = head;
|
||||||
|
len = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addToHead(Node node) {
|
||||||
|
node.next = head.next;
|
||||||
|
head.next.pre = node;
|
||||||
|
head.next = node;
|
||||||
|
node.pre = head;
|
||||||
|
len++;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void deleteNode(Node node) {
|
||||||
|
node.pre.next = node.next;
|
||||||
|
node.next.pre = node.pre;
|
||||||
|
len--;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private int capacity;
|
||||||
|
private int size;
|
||||||
|
private TreeMap<Integer, DLL> freq;
|
||||||
|
private HashMap<Integer, Node> map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* instantiates LFUCache with fixed capacity
|
||||||
|
*
|
||||||
|
* @param capacity The capacity of the cache. Once the cache reaches capacity,
|
||||||
|
* new elements will replace old elements in LFU manner
|
||||||
|
*/
|
||||||
|
public LFUCache(int capacity) {
|
||||||
|
this.capacity = capacity;
|
||||||
|
size = 0;
|
||||||
|
freq = new TreeMap<Integer, DLL>();
|
||||||
|
map = new HashMap<Integer, Node>();
|
||||||
|
System.out.println("LFUCache initialised with capacity: " + capacity);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* To get the cached value for given key
|
||||||
|
*
|
||||||
|
* @param key The key (int) of the expected value
|
||||||
|
* @return corresponding value for input key
|
||||||
|
* @throws NoSuchElementException if key is absent
|
||||||
|
*/
|
||||||
|
public T get(int key) {
|
||||||
|
// Cache hit condition
|
||||||
|
if (map.containsKey(key)) {
|
||||||
|
Node node = map.get(key);
|
||||||
|
System.out.println("Returning value from cache:" + node.toString());
|
||||||
|
DLL dll = freq.get(node.freq);
|
||||||
|
dll.deleteNode(node);
|
||||||
|
if (dll.len == 0)
|
||||||
|
freq.remove(node.freq);
|
||||||
|
node.freq += 1;
|
||||||
|
dll = freq.computeIfAbsent(node.freq, k -> new DLL());
|
||||||
|
dll.addToHead(node);
|
||||||
|
return node.value;
|
||||||
|
}
|
||||||
|
// Cache miss condition
|
||||||
|
throw new NoSuchElementException("No element for key: " + key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* To put a value in LFU cache with corresponding key
|
||||||
|
*
|
||||||
|
* @param key The key (int)
|
||||||
|
* @param value The value to be cached
|
||||||
|
*/
|
||||||
|
public void put(int key, T value) {
|
||||||
|
if (capacity == 0) {
|
||||||
|
System.out.println("Cache set to 0 capacity. No element will be cached");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (map.containsKey(key)) {
|
||||||
|
System.out.println("Key " + key + " already present in cache.Value will be replaced");
|
||||||
|
Node node = map.get(key);
|
||||||
|
node.value = value;
|
||||||
|
DLL dll = freq.get(node.freq);
|
||||||
|
dll.deleteNode(node);
|
||||||
|
if (dll.len == 0)
|
||||||
|
freq.remove(node.freq);
|
||||||
|
node.freq += 1;
|
||||||
|
dll = freq.computeIfAbsent(node.freq, k -> new DLL());
|
||||||
|
dll.addToHead(node);
|
||||||
|
} else {
|
||||||
|
System.out.println("Adding new key " + key + " to cache");
|
||||||
|
Node node = new Node(key, value, 1);
|
||||||
|
map.put(key, node);
|
||||||
|
|
||||||
|
if (size < capacity) {
|
||||||
|
size++;
|
||||||
|
DLL dll = freq.computeIfAbsent(1, k -> new DLL());
|
||||||
|
dll.addToHead(node);
|
||||||
|
} else {
|
||||||
|
System.out.println("Cache at peak capacity.Old values will be removed in LFU fashion");
|
||||||
|
Integer lowest = freq.firstKey();
|
||||||
|
DLL dll = freq.get(lowest);
|
||||||
|
map.remove(dll.tail.pre.key);
|
||||||
|
System.out.println("Value removed:" + dll.tail.pre.value.toString());
|
||||||
|
dll.deleteNode(dll.tail.pre);
|
||||||
|
if (dll.len == 0 && lowest != 1)
|
||||||
|
freq.remove(lowest);
|
||||||
|
DLL freq_one = freq.computeIfAbsent(1, k -> new DLL());
|
||||||
|
freq_one.addToHead(node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
25
src/test/java/com/caching/LFUCacheTest.java
Normal file
25
src/test/java/com/caching/LFUCacheTest.java
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
package com.caching;
|
||||||
|
|
||||||
|
import java.util.NoSuchElementException;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Assertions;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
public class LFUCacheTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testLFUCache() {
|
||||||
|
LFUCache<Integer> cache = new LFUCache<Integer>(2 /* capacity */ );
|
||||||
|
|
||||||
|
cache.put(1, 1);
|
||||||
|
cache.put(2, 2);
|
||||||
|
Assertions.assertEquals(1, cache.get(1)); // returns 1
|
||||||
|
cache.put(3, 3); // evicts key 2
|
||||||
|
Assertions.assertThrows(NoSuchElementException.class, () -> cache.get(2));// throws exception
|
||||||
|
Assertions.assertEquals(3, cache.get(3)); // returns 3.
|
||||||
|
cache.put(4, 4); // evicts key 1.
|
||||||
|
Assertions.assertThrows(NoSuchElementException.class, () -> cache.get(1));// throws exception
|
||||||
|
Assertions.assertEquals(3, cache.get(3)); // returns 3
|
||||||
|
Assertions.assertEquals(4, cache.get(4)); // returns 4
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user