LruCache源码分析

转载自:http://blog.csdn.net/chdjj/article/details/38701509?utm_source=tuicool&utm_medium=referral


这篇文章分析的LruCache并不是jdk中的类,而是来自安卓,熟悉安卓内存缓存的必然对这个类不陌生。
LruCache内部维护的就是一个LinkedHashMap。
下面开始分析LruCache。
注:下面LruCache源码来自support.v4包。
首先是这个类的成员变量:

  1.     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;//丢失次数  
    
LinkedHashMap的初始化放在构造器中:
  1.     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);  
            } 

这里将LinkedHashMap的accessOrder设置为true。
接下来看两个最重要的方法,put和get。首先是put方法:
  1.     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);  
                    previous = map.put(key, value);  
                    if (previous != null) {//之前已经插入过相同的key  
                        size -= safeSizeOf(key, previous);//那么减去该entry的容量,因为发生覆盖  
                    }  
                }  
                if (previous != null) {  
                    entryRemoved(false, key, previous, value);//这个方法默认空实现  
                }  
                trimToSize(maxSize);//若容量超过maxsize,将会删除最近很少访问的entry  
                return previous;  
            }  
    
put方法无非就是调用LinkedHashMap的put方法,但是这里在调用LinkedHashMap的put方法之前,判断了key和value是否为空,也就是说 LruCache不允许空键值。除此之外, put操作被加锁了,所以是线程安全的
既然是缓存,那么必然能够动态删除一些不常用的键值对,这个工作是由trimToSize方法完成的:
    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);  
                previous = map.put(key, value);  
                if (previous != null) {//之前已经插入过相同的key  
                    size -= safeSizeOf(key, previous);//那么减去该entry的容量,因为发生覆盖  
                }  
            }  
            if (previous != null) {  
                entryRemoved(false, key, previous, value);//这个方法默认空实现  
            }  
            trimToSize(maxSize);//若容量超过maxsize,将会删除最近很少访问的entry  
            return previous;  
        }  


这个方法不断循环删除链表首部元素,也就是最近最少访问的元素,直到容量不超过预先定义的最大值为止。
注:LruCache在android.util包中也有一个LruCache类,但是我发现这个类的trimToSize方法是错误的:
  1.     private 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;  
                        }  
                      
                        Map.Entry<K, V> toEvict = null;  
                        for (Map.Entry<K, V> entry : map.entrySet()) {  
                            toEvict = entry;  
                        }  
              
                        if (toEvict == null) {  
                            break;  
                        }  
                        key = toEvict.getKey();  
                        value = toEvict.getValue();  
                        map.remove(key);  
                        size -= safeSizeOf(key, value);  
                        evictionCount++;  
                    }  
                    entryRemoved(true, key, value, null);  
                }  
            } 
    
这里的代码将会循环删除链表尾部,也就是最近访问最多的元素,这是不正确的!所以大家在做内存缓存的时候一定要注意,看trimToSize方法是否有问题。

接下来是get方法:
  1.     public final V get(K key) {  
                if (key == null) {//不允许空键  
                    throw new NullPointerException("key == null");  
                }  
                V mapValue;  
                synchronized (this) {//线程安全  
                    mapValue = map.get(key);//调用LinkedHashMap的get方法  
                    if (mapValue != null) {  
                        hitCount++;//命中次数加1  
                        return mapValue;//返回value  
                    }  
                    missCount++;//未命中  
                }  
          
                V createdValue = create(key);//默认返回为false  
                if (createdValue == null) {  
                    return null;  
                }  
                synchronized (this) {  
                    createCount++;//如果创建成功,那么create次数加1  
                    mapValue = map.put(key, createdValue);//放到哈希表中  
                    if (mapValue != null) {  
                        // There was a conflict so undo that last put  
                        map.put(key, mapValue);  
                    } else {  
                        size += safeSizeOf(key, createdValue);  
                    }  
                }  
                if (mapValue != null) {  
                    entryRemoved(false, key, createdValue, mapValue);  
                    return mapValue;  
                } else {  
                    trimToSize(maxSize);  
                    return createdValue;  
                }  
            }  
    

get方法即根据key在LinkedHashMap中寻找对应的value,此方法也是线程安全的。

以上就是LruCache最重要的部分,下面再看下其他方法:
remove:
  1.     public final V remove(K key) {  
              if (key == null) {  
                  throw new NullPointerException("key == null");  
              }  
              V previous;  
              synchronized (this) {  
                  previous = map.remove(key);//调用LinkedHashMap的remove方法  
                  if (previous != null) {  
                      size -= safeSizeOf(key, previous);  
                  }  
              }  
              if (previous != null) {  
                  entryRemoved(false, key, previous, null);  
              }  
              return previous;//返回value  
          }  
    
sizeof: 这个方法用于计算每个条目的大小,子类必须得复写这个类。
  1.     protected int sizeOf(K key, V value) {//用于计算每个条目的大小  
               return 1;  
           }  
    
snapshot方法, 返回当前缓存中所有的条目集合
  1.     public synchronized final Map<K, V> snapshot() {  
               return new LinkedHashMap<K, V>(map);  
           }  
    

总结:
1.LruCache封装了LinkedHashMap,提供了LRU缓存的功能;
2.LruCache通过trimToSize方法自动删除最近最少访问的键值对;
3.LruCache不允许空键值;
4.LruCache线程安全;
5.继承LruCache时,必须要复写sizeof方法,用于计算每个条目的大小。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值