LRU缓存

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

  

要解决问题:

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

  解决思路:

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

  然而,业务场景需要根据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 gc
			oldNode.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 GC
			last = prev;
			if (prev == null)
				first = null;
			else
				prev.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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值