基于LRU算法的缓存池——阿里笔试题
2014-04-27 21:47
148 查看
这是一题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
LRUCacheTest.java
在进入我的淘宝页面时,此页面需要获取登录的用户的相关信息,在访问量少的情况下,可以采用直接访问数据库的方式,但当访问量太高时,会导致数据库压力过高,因此通常采取的方法为将用户信息进行缓存,在用户数不多的情况下,这个方案还是提供了很大的帮助的,但用户数增多了一点后,出现的问题是缓存占了太多的内存,而经分析,原因是这些缓存中有很多是不访问的用户信息。请写一段存储用户信息的缓存实现代码,并实现当缓存到达一定大小后,如继续新增用户信息,则将最近不访问的用户信息从缓存中踢出。
分析:学过操作系统的都知道实际上题目要我们实现的是一个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) + " "); } } }
相关文章推荐
- LinkedList实现基于LRU算法的缓存
- Android大图加载优化--基于LRU算法的本地文件缓存
- 基于LRU算法的缓存实现
- 基于最少使用频次的LRU,LFU缓存淘汰算法
- 基于LinkedHashMap实现LRU缓存调度算法原理
- Nodejs基于LRU算法实现的缓存处理操作示例
- 基于LinkedHashMap实现LRU缓存调度算法原理
- 简单LRU算法实现缓存大小的限制策略
- 缓存淘汰算法之LRU
- 【阿里】算法工程师笔试题整理(13&14年)
- Java缓存的Lru算法实现---并对Android util类LruCache的改进
- 缓存淘汰算法之LRU
- 使用LRU算法缓存图片
- 采用LRU算法的MemoryCache缓存实现
- 利用LinkedHashMap实现LRU算法缓存
- 两种常见的缓存淘汰算法LFU&LRU
- LRU缓存淘汰算法
- 缓存淘汰算法之LRU
- 算法与数据结构——阿里笔试题(二)
- 缓存淘汰算法--LRU算法