基于LRU算法的缓存池——阿里笔试题

这是一题2011年阿里实习生招聘的笔试题,感觉不错,拿来给大家分享一下,原题如下:

在进入我的淘宝页面时,此页面需要获取登录的用户的相关信息,在访问量少的情况下,可以采用直接访问数据库的方式,但当访问量太高时,会导致数据库压力过高,因此通常采取的方法为将用户信息进行缓存,在用户数不多的情况下,这个方案还是提供了很大的帮助的,但用户数增多了一点后,出现的问题是缓存占了太多的内存,而经分析,原因是这些缓存中有很多是不访问的用户信息。请写一段存储用户信息的缓存实现代码,并实现当缓存到达一定大小后,如继续新增用户信息,则将最近不访问的用户信息从缓存中踢出。

分析:学过操作系统的都知道实际上题目要我们实现的是一个LRU的算法,可以考虑用一个双向链表来存储数据,并用HashTable来记录数据节点,这里之所以用HashTable而不用HashMap,是因为HashTable是线程安全而HashMap不是线程安全的。

下面对get,put,remove方法做一些说明。

1. V get(K key) 方法

  先从HashTable中通过nodes.get(key)获取节点,若为空则直接返回空值;若不为空则将它移到链表头部。

2.void put(K key, V value) 方法

  1)先从HashTable中通过nodes.get(key)获取节点

    2)判断第一步中取到的节点是否为空。若为空则进入3),否则进入4)

    3)判断缓存容器是否已满。若容器已满则从HashTable和链表中移除该节点;否则currentSize加1。完成后新增一个节点

    4)设置节点的key和value,并将节点移动至链表头部,然后存入HashTable。

3.V remove(K key) 方法

    1)先从HashTable中通过nodes.remove(key)并获取移除的节点

  2) 判断第一步中移除的节点是否为空。若为空则直接返回空值;否则从链表中移除该节点,并返回移除的value。


源代码:


LRUCache.java

import java.util.Hashtable;

/**
 * 
 * @author caolijie LRU 缓存
 * @param <K>
 * @param <V>
 */
public class LRUCache<K, V> {

	private int cacheSize; // 缓存池大小
	private Hashtable nodes;// 缓存容器
	private int currentSize;// 当前大小
	private CacheNode<K, V> first;// 链表头
	private CacheNode<K, V> last;// 链表尾

	/**
	 * 链表节点
	 * 
	 * @author caolijie
	 * 
	 */
	class CacheNode<K, V> {
		CacheNode<K, V> prev;// 前一节点
		CacheNode<K, V> next;// 后一节点
		V value;// 值
		K key;// 键

		public CacheNode() {

		}
	}

	public <K, V> LRUCache(int capacity) throws IllegalAccessException {
		if (capacity <= 0) {
			throw new IllegalAccessException(
					"capacity must be positive integer.");
		}
		currentSize = 0;
		cacheSize = capacity;
		nodes = new Hashtable<K, CacheNode>(capacity); // 缓存容器
	}

	/**
	 * 获取缓存中对象
	 * 
	 * @param key
	 * @return
	 */
	public synchronized V get(K key) {
		CacheNode<K, V> node = (CacheNode<K, V>) nodes.get(key);
		if (node != null) {
			moveToHead(node);
			return node.value;
		} else {
			return null;
		}
	}

	/**
	 * 添加缓存
	 * 
	 * @param key
	 * @param value
	 */
	public synchronized void put(K key, V value) {
		CacheNode<K, V> node = (CacheNode<K, V>) nodes.get(key);
		if (node == null) {// 没有命中
			// 缓存容器是否已经超过大小.
			if (currentSize >= cacheSize) {
				if (last != null)// 将最少使用的删除
					nodes.remove(last.key);// 从缓存容器中移除
				removeLast();// 从双向链表中移除最后一项
			} else {
				currentSize++;
			}

			node = new CacheNode<K, V>();
		}
		node.value = value;
		node.key = key;
		// 将最新使用的节点放到链表头,表示最新使用的.
		moveToHead(node);
		nodes.put(key, node);
	}

	/**
	 * 将缓存删除
	 * 
	 * @param key
	 * @return V
	 */
	public synchronized V remove(K key) {
		CacheNode<K, V> node = (CacheNode<K, V>) nodes.get(key);
		if (node != null) {
			if (node.prev != null) {
				node.prev.next = node.next;
			}
			if (node.next != null) {
				node.next.prev = node.prev;
			}
			if (last == node)
				last = node.prev;
			if (first == node)
				first = node.next;
		}
		return node.value;
	}

	public synchronized void clear() {
		first = null;
		last = null;
	}

	/**
	 * 删除链表尾部节点 表示 删除最少使用的缓存对象
	 */
	private synchronized void removeLast() {
		// 链表尾不为空,则将链表尾指向null. 删除连表尾(删除最少使用的缓存对象)
		if (last != null) {
			if (last.prev != null)
				last.prev.next = null;
			else
				first = null;// 链表中只有一个元素
			last = last.prev;
		}
	}

	/**
	 * 移动到链表头,表示这个节点是最新使用过的
	 * 
	 * @param node
	 */
	private synchronized void moveToHead(CacheNode<K, V> node) {
		if (node == first)
			return;
		if (node.prev != null)
			node.prev.next = node.next;
		if (node.next != null)
			node.next.prev = node.prev;
		if (last == node)
			last = node.prev;
		if (first != null) {
			node.next = first;
			first.prev = node;
		}
		first = node;
		node.prev = null;
		if (last == null)
			last = first;
	}

}


LRUCacheTest.java

public class LRUCacheTest {
	public static void main(String[] args) throws IllegalAccessException {
		LRUCache<String, Integer> cache = new LRUCache<String, Integer>(100);
		for (int i = 0; i < 200; i++) {
			cache.put("" + i, i);
		}
		for (int i = 0; i < 200; i++) {
			System.out.print(cache.get("" + i) + "  ");
		}
	}
}




  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值