Skip to content

Commit ba31794

Browse files
committed
MRU cache
1 parent a3630cb commit ba31794

File tree

4 files changed

+433
-3
lines changed

4 files changed

+433
-3
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,4 +112,4 @@ Below topics/problems are covered as of now.
112112
**14. Cache**
113113
- [X] [Introduction](../master/src/com/deepak/data/structures/Cache/CacheIntroduction.md)
114114
- [X] [LRU Cache](../master/src/com/deepak/data/structures/Cache/LRUCache.java)
115-
- [ ] MRU Cache
115+
- [X] [MRU Cache](../master/src/com/deepak/data/structures/Cache/MRUCache.java)

src/com/deepak/data/structures/Cache/LRUCache.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
import java.util.HashMap;
88

99
/**
10-
* Custom implementation if LRU cache
10+
* Custom implementation of LRU cache
1111
*
1212
* @author Deepak
1313
*/
@@ -64,7 +64,7 @@ public V get(K key) {
6464
/* If key exists, get the key and return the value */
6565
Node<K, V> node = cache.get(key);
6666
/* Since this node is least recently used now, move it to the end.
67-
* When eviction will happen, this will be the last entry to be removed.
67+
* When eviction will happen, this will be the first entry to be removed.
6868
* Removal will happen at head. */
6969
moveNodeToLast(node);
7070
return node.getValue();
Lines changed: 289 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,289 @@
1+
/**
2+
* Data-Structures-in-Java
3+
* MRUCache.java
4+
*/
5+
package com.deepak.data.structures.Cache;
6+
7+
import java.util.HashMap;
8+
9+
/**
10+
* Custom implementation of MRU cache
11+
* NOTE : Only the eviction differs in LRU and MRU
12+
*
13+
* @author Deepak
14+
*/
15+
public class MRUCache<K, V> {
16+
17+
/**
18+
* HashMap for cache
19+
*/
20+
private HashMap<K, Node<K, V>> cache = new HashMap<>();
21+
22+
/**
23+
* Nodes for head and tail
24+
*/
25+
private Node<K, V> head, tail;
26+
27+
/**
28+
* Capacity of cache
29+
*/
30+
private int capacity;
31+
32+
/**
33+
* Default capacity of cache
34+
*/
35+
private static final int DEFAULT_CAPACITY = 100;
36+
37+
/**
38+
* Default Constructor
39+
*/
40+
public MRUCache() {
41+
setCapacity(DEFAULT_CAPACITY);
42+
}
43+
44+
/**
45+
* Parameterized Constructor
46+
*
47+
* @param capacity
48+
*/
49+
public MRUCache(int capacity) {
50+
setCapacity(capacity);
51+
}
52+
53+
/**
54+
* Method to get the value associated
55+
* with key from cache
56+
*
57+
* @param key
58+
* @return {@link V}
59+
*/
60+
public V get(K key) {
61+
/* If cache does not contains key, return null */
62+
if (!cache.containsKey(key)) {
63+
return null;
64+
}
65+
/* If key exists, get the key and return the value */
66+
Node<K, V> node = cache.get(key);
67+
/* Since this node is least recently used now, move it to the end.
68+
* When eviction will happen, this will be the last entry to be removed.
69+
* Removal will happen at tail. */
70+
moveNodeToLast(node);
71+
return node.getValue();
72+
}
73+
74+
/**
75+
* Method to add the key value pair to cache
76+
*
77+
* @param key
78+
* @param value
79+
*/
80+
public void put(K key, V value) {
81+
/* If cache contains the key, find that key value pair,
82+
* Since this is a map and only key is same. Value can be different.
83+
* Replace this current value and push this node to end as we have
84+
* accessed it already */
85+
if (cache.containsKey(key)) {
86+
Node<K, V> existing = cache.get(key);
87+
existing.setValue(value);
88+
moveNodeToLast(existing);
89+
return;
90+
}
91+
/* If cache has reached the capacity,
92+
* evict the MRU node and remove it from cache */
93+
Node<K, V> newNode;
94+
if (cache.size() == capacity) {
95+
newNode = evict();
96+
cache.remove(newNode.getKey());
97+
} else {
98+
newNode = new Node<>();
99+
}
100+
/* Create this new node, set key and values.
101+
* Add this node to appropriate place and put in cache */
102+
newNode.setKey(key);
103+
newNode.setValue(value);
104+
addNewNode(newNode);
105+
cache.put(key, newNode);
106+
}
107+
108+
/**
109+
* Method to set capacity for cache
110+
*
111+
* @param capacity
112+
*/
113+
public void setCapacity(int capacity) {
114+
/* Check if capacity is valid */
115+
checkCapacity(capacity);
116+
/* Now, start from the end and begin evicting
117+
* until we meet the new capacity. Remove
118+
* entries from cache as well */
119+
for (int i = cache.size(); i > capacity; i--) {
120+
Node<K, V> evicted = evict();
121+
cache.remove(evicted.getKey());
122+
}
123+
this.capacity = capacity;
124+
}
125+
126+
/**
127+
* Method to check the size of cache
128+
*
129+
* @return {@link int}
130+
*/
131+
public int size() {
132+
return cache.size();
133+
}
134+
135+
/**
136+
* Method to get the capacity of cache
137+
*
138+
* @return {@link int}
139+
*/
140+
public int getCapacity() {
141+
return capacity;
142+
}
143+
144+
/**
145+
* Method to add a new node
146+
*
147+
* @param node
148+
*/
149+
private void addNewNode(Node<K, V> node) {
150+
/* If cache is empty, both head and tail
151+
* points to same new node */
152+
if (cache.isEmpty()) {
153+
head = tail = node;
154+
return;
155+
}
156+
/* Else, append the new node to tail and
157+
* update the position of tail */
158+
tail.setNext(node);
159+
node.setPrevious(tail);
160+
node.setNext(null);
161+
tail = node;
162+
}
163+
164+
/**
165+
* Method to evict the entry from cache
166+
*
167+
* @return {@link Node<K, V>}
168+
*/
169+
private Node<K, V> evict() {
170+
/* If cache is empty, nothing to evict */
171+
if (head == null) {
172+
throw new AssertionError("Cannot evict from an empty cache!!");
173+
}
174+
/* Evict the tail, update next and evicted node */
175+
Node<K, V> evicted = tail;
176+
tail = evicted.getPrevious();
177+
tail.setNext(null);
178+
evicted.setNext(null);
179+
return evicted;
180+
}
181+
182+
/**
183+
* Method to check capacity
184+
*
185+
* @param capacity
186+
*/
187+
private void checkCapacity(int capacity) {
188+
/* Invalid capacity if less then 0 */
189+
if (capacity <= 0) {
190+
throw new IllegalArgumentException("Invalid Capacity. Should be positive!!");
191+
}
192+
}
193+
194+
/**
195+
* Method to move node to last
196+
*
197+
* @param node
198+
*/
199+
private void moveNodeToLast(Node<K, V> node) {
200+
/* If node is already at last, nothing to process */
201+
if (tail == node) {
202+
return;
203+
}
204+
/* Update previous and next nodes */
205+
Node<K, V> previous = node.getPrevious();
206+
Node<K, V> next = node.getNext();
207+
if (previous != null) {
208+
previous.setNext(next);
209+
}
210+
if (next != null) {
211+
next.setPrevious(previous);
212+
}
213+
/* Update head and tail position */
214+
if (head == node) {
215+
head = next;
216+
}
217+
tail.setNext(node);
218+
node.setPrevious(tail);
219+
node.setNext(null);
220+
tail = node;
221+
}
222+
223+
/**
224+
* Class Node for MRU cache
225+
*
226+
* @author Deepak
227+
*
228+
* @param <T>
229+
* @param <U>
230+
*/
231+
public static class Node<T, U> {
232+
233+
/* Variables to store key, value and pointers to next and previous nodes */
234+
private Node<T, U> previous;
235+
private Node<T, U> next;
236+
private T key;
237+
private U value;
238+
239+
/**
240+
* Default Constructor
241+
*/
242+
public Node() {}
243+
244+
/**
245+
* Parameterized Constructor
246+
*
247+
* @param previous
248+
* @param next
249+
* @param key
250+
* @param value
251+
*/
252+
public Node(Node<T, U> previous, Node<T, U> next, T key, U value) {
253+
this.previous = previous;
254+
this.next = next;
255+
this.key = key;
256+
this.value = value;
257+
}
258+
259+
public Node<T, U> getPrevious() {
260+
return previous;
261+
}
262+
public void setPrevious(Node<T, U> previous) {
263+
this.previous = previous;
264+
}
265+
266+
public Node<T, U> getNext() {
267+
return next;
268+
}
269+
public void setNext(Node<T, U> next) {
270+
this.next = next;
271+
}
272+
273+
public T getKey() {
274+
return key;
275+
}
276+
public void setKey(T key) {
277+
this.key = key;
278+
}
279+
280+
public U getValue() {
281+
return value;
282+
}
283+
public void setValue(U value) {
284+
this.value = value;
285+
}
286+
287+
}
288+
289+
}

0 commit comments

Comments
 (0)