android开发-LRU缓存源码解析

来源:互联网 发布:怎么制作mac安装u盘 编辑:程序博客网 时间:2024/06/01 16:17

 LRU ---- least recently used.顾名思义,是根据数据的活跃度进行更新的缓存算法。


Java里面实现LRU缓存通常有两种选择,

一种是使用LinkedHashMap,

一种是自己设计数据结构,使用链表+HashMap(线程不安全) 或者是链表+HashTable(线程安全)


一、LRU Cache的LinkedHashMap实现:

LinkedHashMap自身已经实现了顺序存储,默认情况下是按照元素的添加顺序存储,也可以启用按照访问顺序存储,即最近读取的数据放在最前面,最早读取的数据放在最后面,然后它还有一个判断是否删除最老数据的方法,默认是返回false,即不删除数据,我们使用LinkedHashMap实现LRU缓存的方法就是对LinkedHashMap实现简单的扩展,主要是扩展removeEldestEntry这个方法。

//LinkedHashMap自带的判断是否删除最老的元素方法,默认返回false,即不删除老数据//我们要做的就是重写这个方法,当满足一定条件时删除老数据protected boolean removeEldestEntry(Map.Entry<K,V> eldest) {        return false;}
扩展方式有两种,一种是inheritance(继承),一种是delegation(代理),具体使用什么方式看个人喜好

1、inheritance(继承)
public class LRUCache2<K, V> extends LinkedHashMap<K, V> {    private final int MAX_CACHE_SIZE;    public LRUCache2(int cacheSize) {        super((int) Math.ceil(cacheSize / 0.75) + 1, 0.75f, true);        MAX_CACHE_SIZE = cacheSize;    }    @Override    protected boolean removeEldestEntry(Map.Entry eldest) {        return size() > MAX_CACHE_SIZE;    }    @Override    public String toString() {        StringBuilder sb = new StringBuilder();        for (Map.Entry<K, V> entry : entrySet()) {            sb.append(String.format("%s:%s ", entry.getKey(), entry.getValue()));        }        return sb.toString();    }}

2、delegation(代理)

public class LRUCache3<K, V> {    private final int MAX_CACHE_SIZE;    private final float DEFAULT_LOAD_FACTOR = 0.75f;    LinkedHashMap<K, V> map;    public LRUCache3(int cacheSize) {        MAX_CACHE_SIZE = cacheSize;        //根据cacheSize和加载因子计算hashmap的capactiy,+1确保当达到cacheSize上限时不会触发hashmap的扩容,        int capacity = (int) Math.ceil(MAX_CACHE_SIZE / DEFAULT_LOAD_FACTOR) + 1;        map = new LinkedHashMap(capacity, DEFAULT_LOAD_FACTOR, true) {            @Override            protected boolean removeEldestEntry(Map.Entry eldest) {                return size() > MAX_CACHE_SIZE;            }        };    }    public synchronized void put(K key, V value) {        map.put(key, value);    }    public synchronized V get(K key) {        return map.get(key);    }    public synchronized void remove(K key) {        map.remove(key);    }    public synchronized Set<Map.Entry<K, V>> getAll() {        return map.entrySet();    }    public synchronized int size() {        return map.size();    }    public synchronized void clear() {        map.clear();    }    @Override    public String toString() {        StringBuilder sb = new StringBuilder();        for (Map.Entry entry : map.entrySet()) {            sb.append(String.format("%s:%s ", entry.getKey(), entry.getValue()));        }        return sb.toString();    }}

三、双向链表+HashMap

public class LRUCache1<K, V> {    private final int MAX_CACHE_SIZE;    private Entry first;    private Entry last;    private HashMap<K, Entry<K, V>> hashMap;    public LRUCache1(int cacheSize) {        MAX_CACHE_SIZE = cacheSize;        hashMap = new HashMap<K, Entry<K, V>>();    }    public void put(K key, V value) {        Entry entry = getEntry(key);        if (entry == null) {            if (hashMap.size() >= MAX_CACHE_SIZE) {                hashMap.remove(last.key);                removeLast();            }            entry = new Entry();            entry.key = key;        }        entry.value = value;        moveToFirst(entry);        hashMap.put(key, entry);    }    public V get(K key) {        Entry<K, V> entry = getEntry(key);        if (entry == null) return null;        moveToFirst(entry);        return entry.value;    }    public void remove(K key) {        Entry entry = getEntry(key);        if (entry != null) {            if (entry.pre != null) entry.pre.next = entry.next;            if (entry.next != null) entry.next.pre = entry.pre;            if (entry == first) first = entry.next;            if (entry == last) last = entry.pre;        }        hashMap.remove(key);    }    private void moveToFirst(Entry entry) {        if (entry == first) return;        if (entry.pre != null) entry.pre.next = entry.next;        if (entry.next != null) entry.next.pre = entry.pre;        if (entry == last) last = last.pre;        if (first == null || last == null) {            first = last = entry;            return;        }        entry.next = first;        first.pre = entry;        first = entry;        entry.pre = null;    }    private void removeLast() {        if (last != null) {            last = last.pre;            if (last == null) first = null;            else last.next = null;        }    }    private Entry<K, V> getEntry(K key) {        return hashMap.get(key);    }    @Override    public String toString() {        StringBuilder sb = new StringBuilder();        Entry entry = first;        while (entry != null) {            sb.append(String.format("%s:%s ", entry.key, entry.value));            entry = entry.next;        }        return sb.toString();    }    class Entry<K, V> {        public Entry pre;        public Entry next;        public K key;        public V value;    }}

四、双向链表+HashTable

public class LRUCache {/** * 链表节点 * @author Administrator * */class CacheNode {CacheNode prev;//前一节点CacheNode next;//后一节点Object value;//值Object key;//键CacheNode() {}}public LRUCache(int i) {currentSize = 0;cacheSize = i;nodes = new Hashtable(i);//缓存容器}/** * 获取缓存中对象 * @param key * @return */public Object get(Object key) {CacheNode node = (CacheNode) nodes.get(key);if (node != null) {moveToHead(node);return node.value;} else {return null;}}/** * 添加缓存 * @param key * @param value */public void put(Object key, Object value) {CacheNode node = (CacheNode) nodes.get(key);if (node == null) {//缓存容器是否已经超过大小.if (currentSize >= cacheSize) {if (last != null)//将最少使用的删除nodes.remove(last.key);removeLast();} else {currentSize++;}node = new CacheNode();}node.value = value;node.key = key;//将最新使用的节点放到链表头,表示最新使用的.moveToHead(node);nodes.put(key, node);}/** * 将缓存删除 * @param key * @return */public Object remove(Object key) {CacheNode node = (CacheNode) 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;}public void clear() {first = null;last = null;}/** * 删除链表尾部节点 *  表示 删除最少使用的缓存对象 */private void removeLast() {//链表尾不为空,则将链表尾指向null. 删除连表尾(删除最少使用的缓存对象)if (last != null) {if (last.prev != null)last.prev.next = null;elsefirst = null;last = last.prev;}}/** * 移动到链表头,表示这个节点是最新使用过的 * @param node */private void moveToHead(CacheNode 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;}private int cacheSize;private Hashtable nodes;//缓存容器private int currentSize;private CacheNode first;//链表头private CacheNode last;//链表尾}


其实他们的原理都是差不多,都是双向链表+map的形式,Entry里面 都是包含了key,value,pre,next 这四个元素,只是LinkedHashMap已经实现了这些功能

然后通过访问的时候 改变他们的位置,他们的位置 就是通过修改 next与pre的值实现的

草考了这两篇文章:

LinkedHashMap

http://www.cnblogs.com/lzrabbit/p/3734850.html

双向链表+HashTable

http://gogole.iteye.com/blog/692103



原创粉丝点击