package com.freetymekiyan.datastructures; import java.security.InvalidParameterException; import java.util.HashMap; import java.util.Map; /** * Design and implement a data structure for Least Recently Used (LRU) cache. * It should support the following operations: get and set. * <p> * get(key) - Get the value (will always be positive) of the key if the key * exists in the cache, otherwise return null. (get means use, so we need to update) * <p> * 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. */ public class LRUCache<K, V> { private final int capacity; private Map<K, Node> cache; private Node dummyHead = new Node(); private Node tail = new Node(); public LRUCache(int capacity) { if (capacity <= 0) { throw new InvalidParameterException("Cache capacity should be larger than 0"); } this.capacity = capacity; cache = new HashMap<>(capacity); dummyHead.next = tail; dummyHead.prev = null; tail.prev = dummyHead; tail.next = null; } public void set(K key, V val) { if (cache.containsKey(key)) { Node node = cache.get(key); node.val = val; moveToHead(node); } else { Node newNode = new Node(key, val); addFirst(newNode); cache.put(key, newNode); if (cache.size() > capacity) { K toRemove = removeLast(); cache.remove(toRemove); } } } public V get(K key) { if (!cache.containsKey(key)) { throw new NullPointerException("This key is NOT in cache."); } Node node = cache.get(key); moveToHead(node); return node.val; } private void moveToHead(Node node) { removeNode(node); addFirst(node); } private void removeNode(Node node) { node.prev.next = node.next; node.next.prev = node.prev; } private void addFirst(Node n) { n.prev = dummyHead; n.next = dummyHead.next; n.next.prev = n; n.prev.next = n; } private K removeLast() { K key = tail.prev.key; removeNode(tail.prev); return key; } class Node { Node prev; Node next; K key; V val; public Node() { } public Node(K key, V val) { this.key = key; this.val = val; } } }