LruCache源码解析
来源:互联网 发布:imovie mac 教程 编辑:程序博客网 时间:2024/05/21 06:54
最近正在看android官方文档,看到Displaying Bitmaps Efficiently部分,正好看到LruCache,所以便想了解LruCache的源码。
正文
先从LruCache类的成员变量
private final LinkedHashMap<K, V> map; /** Size of this cache in units. Not necessarily the number of elements. */ private int size;//已经存储的大小 private int maxSize;//规定的最大存储空间 private int putCount;//put的次数 private int createCount;//create的次数 private int evictionCount;//回收的次数 private int hitCount;//命中的次数 private int missCount;//丢失的次数
LruCache成员变量不多,其中最重要的是size,maxSize和map这三个成员变量,前两个用于缓存大小的管理,后一个则用于存放缓存的内容。LruCache保存一个LinkedHashMap(双链表),每当value被访问的时候,此value就会移动到队列的头部,当cache已满的时候加入新的value时,在队列尾巴的value会被回收。
LruCache的构造函数
public LruCache(int maxSize) { if (maxSize <= 0) { throw new IllegalArgumentException("maxSize <= 0"); } this.maxSize = maxSize; this.map = new LinkedHashMap<K, V>(0, 0.75f, true); }
传入maxSize即是我们限定的缓存大小,在构造函数中对map进行了初始化,传入的三个参数中最重要的是第三个参数,将accessOrder设置为true。即访问顺序(从近期访问最少到近期访问最多的顺序来保存元素)符合Lru算法。
如果不是很清楚访问顺序和插入顺序,可以看下
http://www.cnblogs.com/yejg1212/archive/2013/04/01/2992921.html
接下来看LruCache的put方法
public final V put(K key, V value) { if (key == null || value == null) { throw new NullPointerException("key == null || value == null"); } V previous; synchronized (this) { putCount++; size += safeSizeOf(key, value); //新加进来的value将会被放置在链表的尾部,put方法会返回key对应的原value,若没有则返回null previous = map.put(key, value); //如果key对应的value被替换,删除原有value的内存大小 if (previous != null) { size -= safeSizeOf(key, previous); } } if (previous != null) { entryRemoved(false, key, previous, value); } trimToSize(maxSize); return previous; }
上述代码中,可以发现LruCache的put方法主要是调用了HashMap的put方法
@Override public V put(K key, V value) { if (key == null) { return putValueForNullKey(value); } int hash = Collections.secondaryHash(key); HashMapEntry<K, V>[] tab = table; int index = hash & (tab.length - 1); for (HashMapEntry<K, V> e = tab[index]; e != null; e = e.next) { if (e.hash == hash && key.equals(e.key)) { preModify(e); V oldValue = e.value; e.value = value; return oldValue; } } // No entry for (non-null) key is present; create one modCount++; if (size++ > threshold) { tab = doubleCapacity(); index = hash & (tab.length - 1); } addNewEntry(key, value, hash, index); return null; }
addNewEntry以上的代码主要是判断传进来的key是否已经存在,如果存在则更换对应的value并返回上一个value,如果key不存在,则执行addNewEntry方法。
LinkedHashMap继承自HashMap,LinkedHashMap本身并没有重写put方法,而是通过重写addNewEntry方法实现value的添加。
@Override void addNewEntry(K key, V value, int hash, int index) { LinkedEntry<K, V> header = this.header; // Remove eldest entry if instructed to do so. LinkedEntry<K, V> eldest = header.nxt; if (eldest != header && removeEldestEntry(eldest)) { remove(eldest.key); } // Create new entry, link it on to list, and put it into table LinkedEntry<K, V> oldTail = header.prv; LinkedEntry<K, V> newTail = new LinkedEntry<K,V>( key, value, hash, table[index], header, oldTail); table[index] = oldTail.nxt = header.prv = newTail; }
其中红色线段代表nxt,蓝色代表pre(画的丑,不要见怪)
回到LruCache中的put方法,在其中还有一个重要的方法trimToSize(int maxSize)其作用的是移除一直没被调用的项,直到剩余项的数小于请求的大小,如果maxSize传入-1,则清空缓存中的所有对象。
public void trimToSize(int maxSize) { while (true) { K key; V value; synchronized (this) { if (size < 0 || (map.isEmpty() && size != 0)) { throw new IllegalStateException(getClass().getName() + ".sizeOf() is reporting inconsistent results!"); } //如果有剩余空间,就不用移除 if (size <= maxSize) { break; } //离header最近的,即上图中的1 Map.Entry<K, V> toEvict = map.eldest(); if (toEvict == null) { break; } key = toEvict.getKey(); value = toEvict.getValue(); map.remove(key);//移除 size -= safeSizeOf(key, value);//删除其占有的内存 evictionCount++; } entryRemoved(true, key, value, null); } }
将上述的进行总结:往cache中添加value时,会将加入的value添加到双链表的尾部,同时判断当前缓冲的大小是否已经超过了限定的大小,删除最“老”的。
接下来LruCache的get方法:
public final V get(K key) { if (key == null) { throw new NullPointerException("key == null"); } V mapValue; synchronized (this) { mapValue = map.get(key); if (mapValue != null) { hitCount++; return mapValue; } missCount++; } /* * Attempt to create a value. This may take a long time, and the map * may be different when create() returns. If a conflicting value was * added to the map while create() was working, we leave that value in * the map and release the created value. */ V createdValue = create(key); if (createdValue == null) { return null; } synchronized (this) { createCount++; mapValue = map.put(key, createdValue); if (mapValue != null) { // There was a conflict so undo that last put //如果mapValue不为空,则撤销上一步的put操作。 map.put(key, mapValue); } else { size += safeSizeOf(key, createdValue); } } if (mapValue != null) { entryRemoved(false, key, createdValue, mapValue); return mapValue; } else { //每次新加入对象都需要调用trimToSize方法看是否需要回收 trimToSize(maxSize); return createdValue; } }
上面代码中出现的create方法需要我们去重写,但基本上不会去重写该方法,因为如果value丢失我们都会重新去获取。
相同的上述代码中最重要的是map.get(),查看LinkedHashMap的get方法。
@Override public V get(Object key) { /* * This method is overridden to eliminate the need for a polymorphic * invocation in superclass at the expense of code duplication. */ if (key == null) { HashMapEntry<K, V> e = entryForNullKey; if (e == null) return null; if (accessOrder) makeTail((LinkedEntry<K, V>) e); return e.value; } //查询链表中是否已具有该key对应的value,如果有再判读accessOrder是否为true int hash = Collections.secondaryHash(key); HashMapEntry<K, V>[] tab = table; for (HashMapEntry<K, V> e = tab[hash & (tab.length - 1)]; e != null; e = e.next) { K eKey = e.key; if (eKey == key || (e.hash == hash && key.equals(eKey))) { if (accessOrder) makeTail((LinkedEntry<K, V>) e); return e.value; } } return null; }
put方法的功能就是从链表中获取key对应的value(因为不是解析LinkedHashMap所以不具体说明),这里需要关注的是makeTail方法
private void makeTail(LinkedEntry<K, V> e) { // Unlink e e.prv.nxt = e.nxt; e.nxt.prv = e.prv; // Relink e as tail LinkedEntry<K, V> header = this.header; LinkedEntry<K, V> oldTail = header.prv; e.nxt = header; e.prv = oldTail; oldTail.nxt = header.prv = e; modCount++; }
该方法将访问的key的项放到链表的尾部。
总结
可以发现Lru算法的实现是通过LinkedHashMap来实现的,通过将新添加的和最近访问的项放到链表的尾部,当缓存大于限制时,移除头部的项来实现近期最少使用算法。
- LruCache源码解析
- LRUCache源码解析
- LruCache源码解析
- LruCache源码完全解析
- 源码全面解析---LruCache
- LruCache源码解析
- LruCache 源码解析
- LruCache源码解析
- android LruCache源码解析
- LruCache源码解析
- LruCache源码解析
- LruCache源码完全解析
- LruCache 源码解析
- LruCache源码解析
- LruCache 源码解析
- android之LruCache源码解析
- 内存缓存LruCache源码解析
- 源码解析 ——LRUCache
- 3. Longest Substring Without Repeating Characters(难,重要)
- win7 caffe使用笔记——计算图像均值
- leetcode Set Matrix Zeroes
- Android之Http通信HttpConnection
- LA 3644并查集
- LruCache源码解析
- SPOJ COT(树上k大,主席树+LCA)
- FFmpeg音视频同步的问题
- 93.leetcode Restore IP Addresses(medium)[回溯 DFS]
- MySQL for mac 连接报错:Access denied for user 'root'@'localhost' (using password: YES)
- Codeforces Round #368 (Div. 2) D Persistent Bookcase(离线+DFS)
- [2-sat]模型基础(poj3678)
- Day0823_将音乐库中的音乐在Service中异步加载, 播放音乐并设置到通知栏
- pycharm设置