Skip to content

Instantly share code, notes, and snippets.

@Barala
Last active January 2, 2019 13:19
Show Gist options
  • Save Barala/bd837afa68b857b25c430bac24bafd27 to your computer and use it in GitHub Desktop.
Save Barala/bd837afa68b857b25c430bac24bafd27 to your computer and use it in GitHub Desktop.
LRU cache implementation though operations are not atomic
import java.util.HashMap;
/**
*
* HEAD will have least recent data
*
* null values are not supported
*
* @author barala
*
*/
public class LRUCache<K,V> implements ICache<K, V>{
private class LNode<K>{
K key;
LNode<K> prev;
LNode<K> next;
public LNode(K key) {
this.key = key;
}
}
private final int SIZE;
private HashMap<K, V> dataMap = new HashMap<>();
private HashMap<K, LNode<K>> keyNodeMapping = new HashMap<>();
private LNode<K> HEAD, TAIL;
public LRUCache(int size) {
this.SIZE = size;
}
@Override
public void put(K k, V v) {
if(keyNodeMapping.size()==SIZE && isNewKey(k)){
evictLeastRecent();
}
this.dataMap.put(k, v);
updateRecentcy(k);
}
@Override
public V get(K key) {
V value = this.dataMap.get(key);
if(value==null) return value;
updateRecentcy(key);
return value;
}
@Override
public V evict(K key) {
V value = this.dataMap.get(key);
if(value==null) return value;
this.dataMap.remove(key);
removeNode(this.keyNodeMapping.get(key));
this.keyNodeMapping.remove(key);
return value;
}
private void evictLeastRecent(){
K key = HEAD.key;
HEAD.next.prev = null;
HEAD = HEAD.next;
this.dataMap.remove(key);
this.keyNodeMapping.remove(key);
}
private boolean isNewKey(K key){
return !this.dataMap.containsKey(key);
}
private void updateRecentcy(K k){
//case1 where key already exists
LNode<K> existingNode = this.keyNodeMapping.get(k);
if(existingNode == null){
existingNode = new LNode<K>(k);
this.keyNodeMapping.put(k, existingNode);
}
promoteNode(existingNode);
}
private void promoteNode(LNode<K> node){
if(HEAD==null && TAIL==null){
HEAD = node;
TAIL = node;
return;
}
/*
* case1) This node is HEAD node
* case2) This node is TAIL node
* case3) this node is middle node
* case4) this node is new node
*/
//case1
if(node.prev==null && node.next!=null){
node.next.prev = null;
HEAD = node.next;
node.next = null;
TAIL.next = node;
node.prev = TAIL;
TAIL = node;
}
//case2
if(node.prev!=null && node.next==null){
//no need to do anything
return;
}
//case3
if(node.prev!=null && node.next!=null){
node.prev.next = node.next;
node.next.prev = node.prev;
node.next = null;
TAIL.next = node;
node.prev = TAIL;
TAIL = node;
}
//case4
if(node.prev==null && node.next==null){
TAIL.next = node;
node.prev = TAIL;
TAIL = node;
}
}
private void removeNode(LNode<K> node){
/*
* case1) node can be HEAD
* case2) node can be TAIL
* case3) node can be middle node
*
*/
//case1
if(node.prev==null && node.next!=null){
HEAD.next.prev=null;
HEAD = HEAD.next;
return;
}
//case2
if(node.prev!=null && node.next==null){
TAIL = TAIL.prev;
TAIL.next = null;
return;
}
//case3
if(node.prev!=null && node.next!=null){
node.prev.next = node.next;
node.next.prev = node.prev;
}
}
}
interface ICache<K,V> {
public void put(K k, V v);
public V get(K key);
public V evict(K key);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment