- LRU(Least Recently Used ,最近最少使用) —— 删除最久没有被使用过的数据
- LFU(Least Frequently Used,最不经常使用) —— 删除使用次数最少的的数据
- FIFO(First In First Out ,先进先出)
基于现有jdk类库,我们可以很容易实现上面的缓存算法
首先定义缓存接口类
/**
* 缓存接口
* @author Wen
*
*/
public interface Cache<K,V> {
/**
* 返回当前缓存的大小
*
* @return
*/
int size();
/**
* 返回默认存活时间
*
* @return
*/
long getDefaultExpire();
/**
* 向缓存添加value对象,其在缓存中生存时间为默认值
*
* @param key
* @param value
*/
void put(K key ,V value) ;
/**
* 向缓存添加value对象,并指定存活时间
* @param key
* @param value
* @param expire 过期时间
*/
void put(K key ,V value , long expire ) ;
/**
* 查找缓存对象
* @param key
* @return
*/
V get(K key);
/**
* 淘汰对象
*
* @return 被删除对象大小
*/
int eliminate();
/**
* 缓存是否已经满
* @return
*/
boolean isFull();
/**
* 删除缓存对象
*
* @param key
*/
void remove(K key);
/**
* 清除所有缓存对象
*/
void clear();
/**
* 返回缓存大小
*
* @return
*/
int getCacheSize();
/**
* 缓存中是否为空
*/
boolean isEmpty();
}
import java.util.Map;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
* 默认实现
*/
public abstract class AbstractCacheMap<K,V> implements Cache<K,V> {
class CacheObject<K2,V2> {
CacheObject(K2 key, V2 value, long ttl) {
this.key = key;
this.cachedObject = value;
this.ttl = ttl;
this.lastAccess = System.currentTimeMillis();
}
final K2 key;
final V2 cachedObject;
long lastAccess; // 最后访问时间
long accessCount; // 访问次数
long ttl; // 对象存活时间(time-to-live)
boolean isExpired() {
if (ttl == 0) {
return false;
}
return lastAccess + ttl < System.currentTimeMillis();
}
V2 getObject() {
lastAccess = System.currentTimeMillis();
accessCount++;
return cachedObject;
}
}
protected Map<K,CacheObject<K,V>> cacheMap;
private final ReentrantReadWriteLock cacheLock = new ReentrantReadWriteLock();
private final Lock readLock = cacheLock.readLock();
private final Lock writeLock = cacheLock.writeLock();
protected int cacheSize; // 缓存大小 , 0 -> 无限制
protected boolean existCustomExpire ; //是否设置默认过期时间
public int getCacheSize() {
return cacheSize;
}
protected long defaultExpire; // 默认过期时间, 0 -> 永不过期
public AbstractCacheMap(int cacheSize ,long defaultExpire){
this.cacheSize = cacheSize ;
this.defaultExpire = defaultExpire ;
}
public long getDefaultExpire() {
return defaultExpire;
}
protected boolean isNeedClearExpiredObject(){
return defaultExpire > 0 || existCustomExpire ;
}
public void put(K key, V value) {
put(key, value, defaultExpire );
}
public void put(K key, V value, long expire) {
writeLock.lock();
try {
CacheObject<K,V> co = new CacheObject<K,V>(key, value, expire);
if (expire != 0) {
existCustomExpire = true;
}
if (isFull()) {
eliminate() ;
}
cacheMap.put(key, co);
}
finally {
writeLock.unlock();
}
}
/**
* {@inheritDoc}
*/
public V get(K key) {
readLock.lock();
try {
CacheObject<K,V> co = cacheMap.get(key);
if (co == null) {
return null;
}
if (co.isExpired() == true) {
cacheMap.remove(key);
return null;
}
return co.getObject();
}
finally {
readLock.unlock();
}
}
public final int eliminate() {
writeLock.lock();
try {
return eliminateCache();
}
finally {
writeLock.unlock();
}
}
/**
* 淘汰对象具体实现
*
* @return
*/
protected abstract int eliminateCache();
public boolean isFull() {
if (cacheSize == 0) {//o -> 无限制
return false;
}
return cacheMap.size() >= cacheSize;
}
public void remove(K key) {
writeLock.lock();
try {
cacheMap.remove(key);
}
finally {
writeLock.unlock();
}
}
public void clear() {
writeLock.lock();
try {
cacheMap.clear();
}
finally {
writeLock.unlock();
}
}
public int size() {
return cacheMap.size();
}
public boolean isEmpty() {
return size() == 0;
}
}
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* LRU 实现
* @author Wen
*
* @param <K>
* @param <V>
*/
public class LRUCache<K, V> extends AbstractCacheMap<K, V> {
public LRUCache(int cacheSize, long defaultExpire) {
super(cacheSize , defaultExpire) ;
//linkedHash已经实现LRU算法 是通过双向链表来实现,当某个位置被命中,通过调整链表的指向将该位置调整到头位置,新加入的内容直接放在链表头,如此一来,最近被命中的内容就向链表头移动,需要替换时,链表最后的位置就是最近最少使用的位置
this.cacheMap = new LinkedHashMap<K, CacheObject<K, V>>( cacheSize +1 , 1f,true ) {
@Override
protected boolean removeEldestEntry(
Map.Entry<K, CacheObject<K, V>> eldest) {
return LRUCache.this.removeEldestEntry(eldest);
}
};
}
private boolean removeEldestEntry(Map.Entry<K, CacheObject<K, V>> eldest) {
if (cacheSize == 0)
return false;
return size() > cacheSize;
}
/**
* 只需要实现清除过期对象就可以了,linkedHashMap已经实现LRU
*/
@Override
protected int eliminateCache() {
if(!isNeedClearExpiredObject()){ return 0 ;}
Iterator<CacheObject<K, V>> iterator = cacheMap.values().iterator();
int count = 0 ;
while(iterator.hasNext()){
CacheObject<K, V> cacheObject = iterator.next();
if(cacheObject.isExpired() ){
iterator.remove();
count++ ;
}
}
return count;
}
}
import java.util.HashMap;
import java.util.Iterator;
//LFU实现
public class LFUCache<K,V> extends AbstractCacheMap<K, V> {
public LFUCache(int cacheSize, long defaultExpire) {
super(cacheSize, defaultExpire);
cacheMap = new HashMap<K, CacheObject<K,V>>(cacheSize+1) ;
}
/**
* 实现删除过期对象 和 删除访问次数最少的对象
*
*/
@Override
protected int eliminateCache() {
Iterator<CacheObject<K, V>> iterator = cacheMap.values().iterator();
int count = 0 ;
long minAccessCount = Long.MAX_VALUE ;
while(iterator.hasNext()){
CacheObject<K, V> cacheObject = iterator.next();
if(cacheObject.isExpired() ){
iterator.remove();
count++ ;
continue ;
}else{
minAccessCount = Math.min(cacheObject.accessCount , minAccessCount) ;
}
}
if(count > 0 ) return count ;
if(minAccessCount != Long.MAX_VALUE ){
iterator = cacheMap.values().iterator();
while(iterator.hasNext()){
CacheObject<K, V> cacheObject = iterator.next();
cacheObject.accessCount -= minAccessCount ;
if(cacheObject.accessCount <= 0 ){
iterator.remove();
count++ ;
}
}
}
return count;
}
}
import java.util.Iterator;
import java.util.LinkedHashMap;
/**
* FIFO实现
* @author Wen
*
* @param <K>
* @param <V>
*/
public class FIFOCache<K, V> extends AbstractCacheMap<K, V> {
public FIFOCache(int cacheSize, long defaultExpire) {
super(cacheSize, defaultExpire);
cacheMap = new LinkedHashMap<K, CacheObject<K, V>>(cacheSize + 1);
}
@Override
protected int eliminateCache() {
int count = 0;
K firstKey = null;
Iterator<CacheObject<K, V>> iterator = cacheMap.values().iterator();
while (iterator.hasNext()) {
CacheObject<K, V> cacheObject = iterator.next();
if (cacheObject.isExpired()) {
iterator.remove();
count++;
} else {
if (firstKey == null)
firstKey = cacheObject.key;
}
}
if (firstKey != null && isFull()) {//删除过期对象还是满,继续删除链表第一个
cacheMap.remove(firstKey);
}
return count;
}
}
- 解题思路:
- Cache的接口:
- 根据键值查询hashmap,若命中,则返回节点,否则返回null。
- 从双向链表中删除命中的节点,将其重新插入到表头。
- 所有操作的复杂度均为O(1)。
- 将新的节点关联到Hashmap
- 如果Cache满了,删除双向链表的尾节点,同时删除Hashmap对应的记录
- 将新的节点插入到双向链表中头部
- 和查询相似
- 从双向链表和Hashmap中同时删除对应的记录。
- 实现方式
// 双向链表节点 public class DoubleLinkListNode implements Serializable{ private Object value; public DoubleLinkListNode next; public DoubleLinkListNode prev; DoubleLinkListNode(Object value){ this.value = value; } public Object getValue() { return value; } }
public class DoubleLinkList { private int size = 0; private DoubleLinkListNode first; private DoubleLinkListNode last; public DoubleLinkList(){ super(); } // 在链表的尾部的插入一个节点 public synchronized void addLast(DoubleLinkListNode me){ if (first == null) { first = me; }else{ last.next = me; me.prev = last; } last = me; size++; } // 在链表的头部插入一个节点 public synchronized void addFirtst(DoubleLinkListNode me){ if (last == null) { last = me; }else{ first.next = me; me.prev = first; } first = me; size++; } // 获取尾节点 public synchronized DoubleLinkListNode getLast(){ return last; } // 获取头结点 public synchronized DoubleLinkListNode getFirst(){ return first; } // 删除链表指定节点(这个节点一定在链表中) public synchronized boolean remove(DoubleLinkListNode me){ if(me.next == null){ //删除尾节点 if (me.prev == null) { // Make sure it really is the only node before setting head and // tail to null. It is possible that we will be passed a node // which has already been removed from the list, in which case // we should ignore it if ( me == first && me == last ){ first = last = null; } } else { last = me.prev; last.next = null; me.prev = null;// gc 回收需要 } }else if(me.prev == null){ // 头结点 first = me.next; first.prev = null; me.next = null; }else{ // 中间节点 me.prev.next = me.next; me.next.prev = me.prev; me.prev = me.next = null; } size--; return true; } // 删除链表所有元素 // 注意不能只执行 first = last = null,这样会引起 OutOfMemory public synchronized boolean removeAll(){ for(DoubleLinkListNode me=first;me!=null;){ if (me.prev != null) { me.prev = null; } DoubleLinkListNode next = me.next; me = next; } first = last = null; size = 0; return true; } // 删除链表的尾节点 public synchronized DoubleLinkListNode removeLast(){ DoubleLinkListNode temp = last; if (last != null) { remove(last); } return temp; } // 将节点 node 移到头部 public synchronized void makeFirst(DoubleLinkListNode node){ if (node.prev == null) { // already the first node , or not a node return; } node.prev.next = node.next; if (node.next == null) { // last but the first last = node.prev; last.next = null; }else { //neither the last or the first node.next.prev = node.prev; } first.prev = node; node.next = first; node.prev = null; first = node; } public synchronized int size(){ return size; } }
- 先实现一个 map 键值对描述符,如下:
public class LRUElementDescriptor extends DoubleLinkListNode { private Object key; public LRUElementDescriptor(Object key,Object value){ super(value); this.key = key; } public Object getKey() { return key; } public void setKey(Object key) { this.key = key; } }
import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.Map; import java.util.Set; @SuppressWarnings("unchecked") public class LRUMap implements Map { // 在 LRUMap 里,需要使用到 map,DoubleLinkList private DoubleLinkList list = null; private Map map = null; private int maxSize = -1; //cache 容量,< 0 时,表示容量不限 private int chunkSize = 1; //当cache 溢出时,需要删除对象的个数,最好验证 chunkSize < maxSize public LRUMap(){ list = new DoubleLinkList(); map = new HashMap(); } public LRUMap(int size){ this(); this.maxSize = size; } public void clear() { map.clear(); list.removeAll(); } public boolean containsKey(Object key) { return map.containsKey(key); } public boolean containsValue(Object value) { return map.containsValue(value); } //返回 key-value 值,注意不是 key-DoubleLinkListNode public Set entrySet() { Set entries = map.entrySet(); Set unWraped = new HashSet(); Iterator it = entries.iterator(); while (it.hasNext()) { Entry pre = (Entry) it.next(); Entry post = new LRUMapEntry(pre.getKey(),((LRUElementDescriptor)pre.getValue()).getValue());; unWraped.add(post); } return unWraped; } public Object get(Object key) { Object value = null; LRUElementDescriptor elem = (LRUElementDescriptor)map.get(key); if (elem !=null) { value = elem.getValue(); list.makeFirst(elem); } return value; } public boolean isEmpty() { return map.size() == 0; } public Set keySet() { return map.keySet(); } public Object put(Object key, Object value) { LRUElementDescriptor old = null; synchronized (this) { addFirst(key, value); old = (LRUElementDescriptor) map.put(((LRUElementDescriptor)list.getFirst()).getKey(),list.getFirst()); // 若已经存在于缓存里,则删除旧的 if (old != null && ((LRUElementDescriptor)list.getFirst()).getKey().equals(old.getKey())) { list.remove(old); } } // 判断是否溢出,若溢出,则删除最后 chunkSize 个元素 int size = map.size(); if (this.maxSize >=0 && this.maxSize < size) { for(int i = 0;i<chunkSize;i++){ LRUElementDescriptor last = (LRUElementDescriptor) list.getLast(); if (last != null) { map.remove(last.getKey()); }else{ System.err.println("update: remove failed for key:"+last.getKey()); } list.removeLast(); } } // Map.put 操作的返回值 if(old != null){ return old.getValue(); } return null; } public synchronized void addFirst (Object key,Object value){ LRUElementDescriptor elem = new LRUElementDescriptor(key,value); list.addFirtst(elem); } public void putAll(Map source) { if (source != null) { Set entries = source.entrySet(); Iterator it = entries.iterator(); while (it.hasNext()) { Entry elem = (Entry) it.next(); // 这里不是使用 map.put,因为这样 DoubleLinkList 就会失去作用 this.put(elem.getKey(), elem.getValue()); } } } public Object remove(Object key) { LRUElementDescriptor elem =(LRUElementDescriptor) map.remove(key); if (elem != null) { list.remove(elem); return elem.getValue(); } return null; } public int size() { return map.size(); } // DoubleLinkListNode 的集合 public Collection values() { return map.values(); } }