Java缓存的Lru算法实现---并对Android util类LruCache的改进

一、 概述

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: [email protected]
 * @date 创建时间:2016-4-22 下午2:51:42
 * @version 1.0
 * @Description:Lru算法的缓存
 */
public class LruCache {
    private LinkedHashMap 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(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);
    }



}

你可能感兴趣的:(算法)