一、 概述
1.LRU算法概述
LRU ( Least Recently Used )叫做最近最久未使用算法,它只是的是一种置换策略算法,计算机系统内存中页面置换基本是这算法。其实这算法也不难理解,就是优先释放不常用的空间给需要的程序。
2.HashMap概述
HashMap实现了Map接口,它的基本特征就是键值对的形式。而key是通过哈希表(Hash table)的结构存储的,因此通过key对HashMap 读取的时间复杂度是O(1).
3.LinkedHashMap概述
LinkedHashMap继承了HashMap,所以有HashMap的特征,但是它给Key增加了两个指针,分别指向节点的前一个和后一个,形成一个双向链表。
二、使用LinkedHashMap实现LRU算法。
经过上面介绍我们知道了LinkedHashMap的数据结构,那么它对我们实现LRU算法有什么好处呢?
首先,缓存的使用目的就是为了读取更快,不需要重复到数据库,硬盘或网络读取。假设我们使用ArrayList实现,将头部移除,那么需要移动n个。这样的时间复杂度就是O(n)了,而LinkedHashMap是O(1).
然后就是直接使用HashMap呢?由于LRU算法要删除的是最近最久未使用的那个,那么HashMap无法知道哪个是最近最久未用的。
ok那么我们就可以像下面那么用了
final int maxSize = 3;
/*参数解释:0 初始大小
* 0.75f 增长因子,一般0.75
* true true表示map.get后将此元素移到尾端,false表示不移动,按照FIFO方式排序
*/
LinkedHashMap<String, String> map = new LinkedHashMap<String, String>(0,0.75f,true){
/* 重写替换条件的方法
* 返回true时将删除顶端节点
*/
@Override
protected boolean removeEldestEntry(
java.util.Map.Entry<String, String> eldest) {
// TODO Auto-generated method stub
return size()>maxSize;
}
};
map.put("1", "3");
但是。。
LinkedHashMap这个类并非线程安全的,对于多线程来说这是会出问题的。
OK,其实Android 的util包中封装了一个类LruCache,它就是使用LinkedHashMap实现的,并且做了线程安全处理。
三、Android util类LruCache的改进
有了Android提供的是不是完美了呢?我觉得不是,经过对源码的研究,我发现LruCache的同步都是用
`synchronized(this){
...
}
这样的方式进行线程同步的,那么它对缓存的读写都是锁住的。鉴于我们的缓存主要是用于读的,而且一写多读不会出现问题,那么我们可以对其进行改进,实现一写多读的功能。刚好Java api中提供了ReadWriteLock这个类。这样我们就可以方便的实现了。
下面就是缓存的完整代码,也可以点这里下载
package com.cache;
import java.util.LinkedHashMap;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
* @author 作者 E-mail: yufemgg@gmail.com
* @date 创建时间:2016-4-22 下午2:51:42
* @version 1.0
* @Description:Lru算法的缓存
*/
public class LruCache<K, V> {
private LinkedHashMap<K, V> map;
private int maxSize;
private int size;
/*
* 需要统计缓存命中率时加入
private int hitCount;
private int missCount;
private int evictionCount;
private int putCount;
*/
private ReadWriteLock mReadWriteLock = new ReentrantReadWriteLock();
public LruCache(int maxSize) {
if (maxSize <= 0) {
throw new IllegalArgumentException("maxSize <= 0");
}
this.size = 0;
this.maxSize = maxSize;
map = new LinkedHashMap<K, V>(0, 0.75f, true);
}
public V put(K key, V value) {
if (key == null || value == null) {
throw new NullPointerException("key==null || value==null");
}
Lock wLock = mReadWriteLock.writeLock();
wLock.lock();//写锁,这是其他线程不可读也不可写
V oldValue = map.put(key, value);
if (oldValue == null) {
size++;
}
if (size > maxSize) {
trimSize(maxSize);
}
wLock.unlock();//释放写锁
return oldValue;
}
public V get(K key) {
Lock rLock = mReadWriteLock.readLock();
rLock.lock();//读锁,其他线程可以读,但不可写
V value = null;
if (map.containsKey(key)) {
value = map.get(key);
if (value == null) {
map.remove(key);
size--;
}
}
rLock.unlock();//释放读锁
return value;
}
public void trimSize(int maxSize) {
if (map != null) {
while (true) {
if (size <= maxSize) {
break;
}
K key = map.keySet().iterator().next();
map.remove(key);
size--;
}
}
}
public void clear(){
Lock wLock = mReadWriteLock.writeLock();
wLock.lock();
trimSize(0);
wLock.unlock();
}
public int getSize() {
return size;
}
@Override
public String toString() {
return String.format("LruCache@size:%d maxSize:%d", size, maxSize);
}
}