LRU缓存

来源:互联网 发布:科比两次得分王数据 编辑:程序博客网 时间:2024/06/05 08:13

以下只是要求必须删除最近最少使用的数据的算法。实际项目中若并发量大,有性能问题。

  

要解决问题:

  有一个有界缓存,要超过缓存容量时,优先清除最近最少使用的元素

  解决思路:

  用双向链表保存元素,头部存储热点元素,尾部是不太使用的元素,每次某个元素做操作时将其放入头部(原先位置元素删除)。

  然而,业务场景需要根据key去查找元素,因此需要有一个map,key就是业务key,value存储了双向链接的结点(结点包含value值)。


  存储一个不存在的key时,会先往链表放入一个元素(生成一个结点),然后往map中放入这个结点。

  之后若对该key做set, get操作时,将元素从当前元素所在位置移到首结点。

 代码如下:

  

import java.util.Map;import java.util.NoSuchElementException;import java.util.concurrent.ConcurrentHashMap;import java.util.concurrent.locks.Lock;import java.util.concurrent.locks.ReentrantLock;public class LRUCache {/** * 存储每个元素,靠近头部的为最近使用比较多的 */private LinkedList<NodeValue /* key,value object */> list;/** * 存储key与 */private Map<Integer, Node<NodeValue>> map;private int limit;static class NodeValue {private Integer key;private Integer value;public NodeValue(Integer key, Integer value) {this.key = key;this.value = value;}public Integer getKey() {return key;}public void setKey(Integer key) {this.key = key;}public Integer getValue() {return value;}public void setValue(Integer value) {this.value = value;}}private Lock lock = new ReentrantLock();public LRUCache(int capacity) {if (capacity <= 0) {throw new IllegalArgumentException("capacity必须大于0");}list = new LinkedList<NodeValue>();map = new ConcurrentHashMap<Integer, Node<NodeValue>>(capacity);limit = capacity;}public int get(int key) {Node<NodeValue> node = map.get(key);if (node == null) {return -1;}int value = node.item.getValue();this.refresh(key, node);return value;}/** *  * refesh *  * @param key * @param oldNode */private void refresh(int key, Node<NodeValue> oldNode) {lock.lock();if (oldNode.item.getKey().intValue() == list.first.item.getKey()) {/** * 当前节点是头结点就需要再进行交换了 */return;}try {/** * 1.删除原先结点 */list.removeNode(oldNode);/** * 2.将这个结点放入头部 */int value = oldNode.item.getValue();this.addNodeToFirst(key, value);// help gcoldNode.next = null;oldNode.item = null;oldNode.prev = null;oldNode = null;} finally {lock.unlock();}}private void addNodeToFirst(int key, int value) {NodeValue nodeValue = new NodeValue(key, value);list.addFirst(nodeValue);Node<NodeValue> freshNode = list.getNode(0);map.put(key, freshNode);}private void resize() {// 必要时进行扩容int oldSize = map.size();if (oldSize >= limit) {/** * 超过容量了,这时需要清除尾结点,空出一个位置 */Node<NodeValue> lastNode = list.getLastNode();int rmKey = lastNode.item.getKey();list.removeNode(lastNode);map.remove(rmKey);}}/** * 设置key,value *  * @param key * @param value */public void set(int key, int value) {lock.lock();try {Node<NodeValue> oldNode = map.get(key);if (oldNode == null) {/** * 必要时扩容 */this.resize();/** * 未发现有这个元素,则可以将其放入list头部,并存储key与这个node的关系到map */this.addNodeToFirst(key, value);} else {/** * 有了这个元素,则需要找到这个key在list中的结点,删除它并将其放入头部 *//** * 1.删除原先结点 */list.removeNode(oldNode);/** * 2.将这个结点放入头部 */this.addNodeToFirst(key, value);}} finally {lock.unlock();}}static class LinkedList<E> {transient int size = 0;transient Node<E> first;transient Node<E> last;public LinkedList() {}private void linkFirst(E e) {Node<E> f = first;Node<E> newNode = new Node<E>(null, e, f);first = newNode;if (f == null) {last = newNode;} else {f.prev = newNode;}size++;}private E unlinkLast(Node<E> l) {final E element = l.item;final Node<E> prev = l.prev;l.item = null;l.prev = null; // help GClast = prev;if (prev == null)first = null;elseprev.next = null;size--;return element;}/** * 删除一个结点 *  * @param oldNode */public void removeNode(Node<E> oldNode) {Node<E> preNode = oldNode.prev;Node<E> nextNode = oldNode.next;if (preNode != null) {preNode.next = nextNode;} else {/** * 当前结点是头结点,这时头结点指向要删除结点下一个 */first = nextNode;}if (nextNode != null) {nextNode.prev = preNode;} else {/** * 要删除结点是尾结点 */// 尾结点始终指向删除指点的上一个结点this.last = preNode;}this.size = this.size - 1;}public Node<E> getLastNode() {final Node<E> l = last;if (l == null)throw new NoSuchElementException();return l;}/** * Removes and returns the last element from this list. * * @return the last element from this list * @throws NoSuchElementException *             if this list is empty */public E removeLast() {final Node<E> l = last;if (l == null)throw new NoSuchElementException();return unlinkLast(l);}/** * Inserts the specified element at the beginning of this list. * * @param e *            the element to add */public void addFirst(E e) {linkFirst(e);}/** * Returns the number of elements in this list. * * @return the number of elements in this list */public int size() {return size;}public Node<E> getNode(int index) {checkElementIndex(index);return node(index);}/** * Tells if the argument is the index of an existing element. */private boolean isElementIndex(int index) {return index >= 0 && index < size;}private String outOfBoundsMsg(int index) {return "Index: " + index + ", Size: " + size;}private void checkElementIndex(int index) {if (!isElementIndex(index))throw new IndexOutOfBoundsException(outOfBoundsMsg(index));}/** * Returns the (non-null) Node at the specified element index. */Node<E> node(int index) {if (index < (size >> 1)) {Node<E> x = first;for (int i = 0; i < index; i++)x = x.next;return x;} else {Node<E> x = last;for (int i = size - 1; i > index; i--)x = x.prev;return x;}}}static class Node<E> {E item;Node<E> next;Node<E> prev;Node(Node<E> prev, E element, Node<E> next) {this.item = element;this.next = next;this.prev = prev;}}}

  


0 0