Wednesday 2 August 2017

LRU caching implementation in Java


Designing a Least recently used cache is an important piece in the software development where an element is removed from the  cache if the size of the cache is reaches to the capacity.

Caching is always helpful in the faster retrieval  and gives O(1) or constant time retrieval complexity.

Map data structure are helpful in achieving the O(1) time complexity and other data structures like Queue or Stack could be a solution where pop or remove method are useful in adding new element in the data structure in a constant time  or using doubly linked list could also be one solution for this caching.

To provide a solution , I have used an implementation using the LinkedHashMap which has the property of giving the least recently used element with the iterator . First element is always an LRU element in the map.

I will demonstrate the same using the following code snippet :

import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Map.Entry;

/**
 * @author sachin.srivastava
 * tek9g.blogspot.com
 *
 */
public class LRUCaching<T> {
private int capacity;
private LinkedHashMap<String, T> map;

public LRUCaching(int capacity) {
this.capacity = capacity;
map = new LinkedHashMap<>();
}
public T get(String key){
T t = this.map.get(key);
if(t == null){
return null;
} else {
this.set(key, t);
return t;
}
}
public void set(String key,T value){
if(this.map.containsKey(key)){
this.map.remove(key);
} else if(this.map.size() == this.capacity){
Iterator<String> it =  this.map.keySet().iterator();
it.next();
it.remove();
}
this.map.put(key, value);
}
public static <T> void printMap(Map<String, T> map){
System.out.println("\n--printing the map elements--\n");
for (Entry<String, T> entry : map.entrySet()) {
System.out.println("key=" + entry.getKey() + " and value=" + entry.getValue());
}
}
public static void main(String[] args) {
LRUCaching<Integer> lru = new LRUCaching<>(5);//capacity of 5 elements.
lru.set("a", 1);
lru.set("b", 2);
lru.set("c", 3);
lru.set("d", 4);
lru.set("e", 5);
printMap(lru.map);
System.out.println("\nget key=a and value=" + lru.get("a"));// it will move key a to front and b would be at the rear.
printMap(lru.map);
//setting the element beyond the capacity
lru.set("f", 6);//it will remove the least used key b
printMap(lru.map);
}

}


Output : 


--printing the map elements--

key=a and value=1
key=b and value=2
key=c and value=3
key=d and value=4
key=e and value=5

get key=a and value=1

--printing the map elements--

key=b and value=2
key=c and value=3
key=d and value=4
key=e and value=5
key=a and value=1

--printing the map elements--

key=c and value=3
key=d and value=4
key=e and value=5
key=a and value=1
key=f and value=6

--------------------------------------

In the above example the Map always contains the number of elements as per the capacity and as it reaches the limit , it removes the Least Recently Used element from the map. As soon as an element is get from the map , it ranks higher in the map so is the case when  key 'a' is get , it moves the key in the above to key 'b' which becomes the LRU element so when adding a new key 'f' it shows the capacity is reached and hence the LRU element which is removed from the map and allows a new key to inserted in the map.

Enjoy the coding!!!!

Tuesday 1 August 2017

Producer and Consumer problem in Java


Solving a Producer and Consumer problem  in a multithreading environment following illustration is sited, I have tried to solve the problem using a Queue,  Producer producing the counter and Consumer consumes the counter :


import java.util.LinkedList;
import java.util.Queue;

/**
 * @author sachin.srivastava
 * tek9g.blogspot.com
 *
 */
public class ProducerConsumer extends Thread{

static Queue<Integer> queue = new LinkedList<>();
private static final int capacity = 10;
private static  int counter = 1;
public static void main(String[] args) {
///starting the producer and consumer
new Producer().start();
new Consumer().start();
}

static class Producer extends Thread {
public void run(){
for ( ;;) {
try {
Thread.sleep(2000);
synchronized (queue) {
if( queue.size() <= capacity){
queue.add(counter);
System.out.println("queue added counter produced=" + counter);
counter++;
} else if(queue.size() > capacity)  {
queue.remove(queue.size()-1);
System.out.println("queue is full so can't produce new element");
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
static class Consumer extends Thread {
public void run(){
for (;;) {
try {
Thread.sleep(2000);
synchronized (queue) {
if(!queue.isEmpty()){
Integer head = queue.remove();
System.out.println("queue pop counter consumer=" + head);
} else {
System.out.println("queue is empty so nothing to pop for consumers");
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}



Output : 


queue added counter produced=1
queue pop counter consumer=1
queue added counter produced=2
queue pop counter consumer=2
queue added counter produced=3
queue pop counter consumer=3
queue added counter produced=4
queue pop counter consumer=4
queue added counter produced=5
queue pop counter consumer=5
queue added counter produced=6
queue pop counter consumer=6
queue added counter produced=7
queue pop counter consumer=7
queue added counter produced=8
queue pop counter consumer=8
queue added counter produced=9
queue pop counter consumer=9
queue added counter produced=10
queue pop counter consumer=10
queue added counter produced=11
queue pop counter consumer=11
queue added counter produced=12
queue pop counter consumer=12
queue added counter produced=13
queue pop counter consumer=13
queue added counter produced=14
queue pop counter consumer=14
queue added counter produced=15
queue pop counter consumer=15
queue added counter produced=16
queue pop counter consumer=16
queue added counter produced=17
queue pop counter consumer=17
queue added counter produced=18
queue pop counter consumer=18
queue added counter produced=19
queue pop counter consumer=19
queue added counter produced=20
queue pop counter consumer=20
queue added counter produced=21
queue pop counter consumer=21
queue added counter produced=22
queue pop counter consumer=22
queue added counter produced=23
queue pop counter consumer=23
queue added counter produced=24
queue pop counter consumer=24
queue added counter produced=25
queue pop counter consumer=25
queue added counter produced=26
queue pop counter consumer=26
queue added counter produced=27
queue pop counter consumer=27
queue added counter produced=28
queue pop counter consumer=28
queue added counter produced=29
queue pop counter consumer=29
--------


If you seen the output  starts with the producer 1  then consumer 1  then  producer 2 then consumer 2 and so on ... so the queue always  remain  in the consistent state and also remain thread safe.

Enjoy the coding  !!!!