Last active
January 2, 2019 13:19
-
-
Save Barala/bd837afa68b857b25c430bac24bafd27 to your computer and use it in GitHub Desktop.
LRU cache implementation though operations are not atomic
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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