LeetCode – LRU Cache (Java)

Design and implement a data structure for Least Recently Used (LRU) cache. It should support the following operations: get and set.

get(key) - Get the value (will always be positive) of the key if the key exists in the cache, otherwise return -1.
set(key, value) - Set or insert the value if the key is not already present. When the cache reached its capacity, it should invalidate the least recently used item before inserting a new item.

Analysis

The key to solve this problem is using a double linked list which enables us to quickly move nodes.

LRU-Cache

The LRU cache is a hash table of keys and double linked nodes. The hash table makes the time of get() to be O(1). The list of double linked nodes make the nodes adding/removal operations O(1).

Java Solution

Define a double linked list node.

class Node{
    int key;
    int value;
    Node pre;
    Node next;
 
    public Node(int key, int value){
        this.key = key;
        this.value = value;
    }
}
public class LRUCache {
    int capacity;
    HashMap<Integer, Node> map = new HashMap<Integer, Node>();
    Node head=null;
    Node end=null;
 
    public LRUCache(int capacity) {
        this.capacity = capacity;
    }
 
    public int get(int key) {
        if(map.containsKey(key)){
            Node n = map.get(key);
            remove(n);
            setHead(n);
            return n.value;
        }
 
        return -1;
    }
 
    public void remove(Node n){
        if(n.pre!=null){
            n.pre.next = n.next;
        }else{
            head = n.next;
        }
 
        if(n.next!=null){
            n.next.pre = n.pre;
        }else{
            end = n.pre;
        }
 
    }
 
    public void setHead(Node n){
        n.next = head;
        n.pre = null;
 
        if(head!=null)
            head.pre = n;
 
        head = n;
 
        if(end ==null)
            end = head;
    }
 
    public void set(int key, int value) {
        if(map.containsKey(key)){
            Node old = map.get(key);
            old.value = value;
            remove(old);
            setHead(old);
        }else{
            Node created = new Node(key, value);
            if(map.size()>=capacity){
                map.remove(end.key);
                remove(end);
                setHead(created);
 
            }else{
                setHead(created);
            }    
 
            map.put(key, created);
        }
    }
}
Category >> Algorithms >> Interview  
If you want someone to read your code, please put the code inside <pre><code> and </code></pre> tags. For example:
<pre><code> 
String foo = "bar";
</code></pre>
  • rc

    Hi,

    Your algorithm is actually quite good.

    There is only a few details to make it better.

    1) When the cache is reaches its max capacity you only remove the end of the list. You should also remove the entry from the hash table.

    if(map.size()>=capacity)
    {
    map.remove(end.key);
    remove(end);
    setHead(newnode);
    }

    2) Don’t forget to update capacity every time you add a new node.

    Cheers,

    RC

  • Alan

    Would I be penalized if I just used an ArrayList instead of creating a doubly linked list? Is there a reason why a double linked list is used in terms of efficiency? Is it because the ArrayList remove function takes O(n) time?

    public class LRUCache {
    HashMap LRU = new HashMap();
    private int Capacity;
    private ArrayList leastRecent = new ArrayList();

    public LRUCache(int capacity) {
    this.Capacity = capacity;
    }

    public int get(int key) {
    if(LRU.get(key) == null){
    return -1;
    }
    else{
    leastRecent.remove((Integer) key);
    leastRecent.add(key);
    return LRU.get(key);
    }
    }

    public void set(int key, int value) {
    if(LRU.get(key) == null){
    if(LRU.size() < Capacity){
    LRU.put(key, value);
    leastRecent.add(key);
    }
    else{
    int remove = leastRecent.remove(0);
    LRU.remove(remove);
    LRU.put(key, value);
    leastRecent.add(key);
    }
    }
    else{
    if(LRU.size() < Capacity){
    leastRecent.remove((Integer) key);
    LRU.put(key, value);
    leastRecent.add(key);
    }
    else{
    leastRecent.remove((Integer) key);
    LRU.put(key, value);
    leastRecent.add(key);
    }
    }
    }
    }

  • Miral

    LinkedHashMap – keeps track of the order in which each entry is added.
    By default, it removes the oldest entry when reached to a threshold.

    1. In constructor – true flag – we are saying that, we want to remove the oldest element based on its access. (the one that was least accessed, should be removed)

    2. In overridden method, we are saying that, remove entry only when we have reached cacheSize.

    Hope this helps.

  • Sagar Dafle

    Hi, Can you please explain how the above code works ?

  • Sagar Dafle

    Hi, Can you please explain how the above code works exactly?

  • Ankit Shah

    because its not first in first out, its least recently used, the node which does a get access needs to be evicted and put in as a head because head grows from left to right and it will be the last element to be removed from.

  • Preetham

    Hi, how can I decide the size of the cache ?

  • why not using LinkedList to implement queue ?

  • aking

    Instead of doing:

    end = end.pre;
    if (end != null) {
    end.next = null;
    }

    Can’t we just make a call to:

    removeNode(end);

  • jubin kuriakose

    public void printKeyPriority(){

    DoubleLinkedListNode node = head.next;
    System.out.print(head.toString());
    while (node !=null){
    System.out.print(node.toString());
    node = node.next;
    }
    System.out.println(“n”);
    }

    call this after a get() / set()

  • ryanlr

    removed. Thanks!

  • Wang Shuhao

    Can you please remove the bar on the left side? cuz it covers the contents. or name it be able to be closed?

  • pinkyrjk

    public static void main(String[] args)

    {

    LRUCache lr=new LRUCache(5);

    lr.set(1, 1);

    lr.set(2,2);

    lr.set(3, 3);

    lr.set(4, 4);

    lr.set(5, 5);

    int val=lr.get(1);

    System.out.println(“”+val);

    lr.set(6, 6);

    int val2=lr.get(2);

    System.out.println(“”+val2);

    }

  • max

    how do you implement this for test?

  • Miral

    public class LRUCache extends LinkedHashMap{

    private int cacheSize;

    public LRUCache(int size) {
    super(size, 0.75f, true);
    this.cacheSize = size;
    }

    @Override
    protected boolean removeEldestEntry(
    java.util.Map.Entry eldest) {

    // remove the oldest element when size limit is reached
    return size() > cacheSize;
    }

    }

  • CodeCode54