LRUCache源码解析

Posted z_fishLong

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了LRUCache源码解析相关的知识,希望对你有一定的参考价值。

package com.example.helloworld;  
  
import java.util.LinkedHashMap;  
import java.util.Map;  
  
/** 
 * Cache保存一个强引用来限制内容数量,每当Item被访问的时候,此Item就会移动到队列的头部。
 * 当cache已满的时候加入新的item时,在队列尾部的item会被回收。
 * 
 * 如果你cache的某个值需要明确释放,重写entryRemoved()
 *  
 * 如果key相对应的item丢掉啦,重写create().这简化了调用代码,即使丢失了也总会返回。
 * 
 * 默认cache大小是测量的item的数量,重写sizeof计算不同item的大小。
 * 
 *   int cacheSize = 4 * 1024 * 1024; // 4MiB 
 *   LruCache<String, Bitmap> bitmapCache = new LruCache<String, Bitmap>(cacheSize)  
 *       protected int sizeOf(String key, Bitmap value)  
 *           return value.getByteCount(); 
 *        
 *   
 * 
 * 这个类是安全的,并发操作时有同步块控制
 * This class is thread-safe. Perform multiple cache operations atomically by 
 * synchronizing on the cache:    
 * 
 *   synchronized (cache)  
 *     if (cache.get(key) == null)  
 *         cache.put(key, value); 
 *      
 *    
 * 
 * ·不允许key或者value为null
 *  当get(),put(),remove()返回值为null时,key相应的项不在cache中
 *  
 *  最少最近使用算法
 */ 
public class LruCache<K, V>   
	
    private final LinkedHashMap<K, V> map; //LruCache 本质上是一个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;      //丢失的次数
  
    
    /** 
     * maxSize规定了缓存的最大值,缓存实体的最大值不能超过maxSize
     */  
    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);  
      
  
    
    
    /** 
     * 通过key返回相应的item,或者创建返回相应的item。
     * 相应的item会移动到队列的头部,
     * 如果item的value没有被cache或者不能被创建,则返回null。
     */  
    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++; 		 //丢失
          
  
        
        /* 
         *  根据key没找到item -->就试图创建一个item 
         *  实际中 create()一般由我们来实现。  LruCache的create()方法默认返回null
         */  
  
        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  
                //如果前面存在oldValue,那么撤销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;  
        
        
      


    
    
    /** 
     * 存入元素--> 刚存入的元素在最前面 
     * 返回值:先前存的值  之前没有值返回null
     */  
    
    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);   //map-->linkedHashMap
            if (previous != null)   		  //返回的先前的value值
                size -= safeSizeOf(key, previous);  
              
          
  
        if (previous != null)                
            entryRemoved(false, key, previous, value); //当新值替换原来的值时调用的方法  默认实现为空  
          
  
        trimToSize(maxSize);                 //重新缓存区计算大小
        return previous;  
      
  
    /** 
     * @param maxSize the maximum size of the cache before returning. May be -1 
     *     to evict even 0-sized elements. 
     *   清空cache空间 
     */  
    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 = 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);  
          
      
  
    /** 
     * Removes the entry for @code key if it exists. 
     * 删除key相应的cache项,返回相应的value
     * @return the previous value mapped by @code key. 
     */  
    public final V remove(K key)   
        if (key == null)   
            throw new NullPointerException("key == null");  
          
  
        V previous;  
        synchronized (this)   
            previous = map.remove(key);  
            if (previous != null)   
                size -= safeSizeOf(key, previous);  
              
          
  
        if (previous != null)   
            entryRemoved(false, key, previous, null);  
          
  
        return previous;  
      
  
    /** 
     * 当item被回收或者删掉时调用。
     * 改方法当value被回收释放存储空间时被remove调用,
     * 或者替换item值时put调用,默认实现什么都没做。
     * 
     * 这个方法没有同步 当这个方法执行时 其他线程可以访问缓存
     * 
     * true---为释放空间被删除;
     * 
     * false---put或remove导致
     * @param newValue the new value for  key, if it exists. If non-null, 
     *     this removal was caused by a  put. Otherwise it was caused by 
     *     an eviction or a remove. 
     *     
     */  
    protected void entryRemoved(boolean evicted, K key, V oldValue, V newValue)   
  
    /** 
     * 用户自己实现 
     * 当某Item丢失时会调用到,返回计算的相应的value或者null
     * 这个方法没有同步 当这个方法执行时 其他线程可以访问缓存
     */  
    protected V create(K key)   
        return null;  
      
  
    //检查size 是否合理
    private int safeSizeOf(K key, V value)   
        int result = sizeOf(key, value);  
        if (result < 0)   
            throw new IllegalStateException("Negative size: " + key + "=" + value);  
          
        return result;  
      
  
    /** 
     * 返回用户定义的item的大小,默认返回1代表item的数量,最大size就是最大item值
     * 一般用户自己实现
     */  
    protected int sizeOf(K key, V value)   
        return 1;  
      
  
    
    
    /** 
     * Clear the cache, calling entryRemoved on each removed entry. 
     * 清空cacke
     */  
    public final void evictAll()   
        trimToSize(-1); 	// -1 will evict 0-sized elements  
      
  
    /** 
     * For caches that do not override @link #sizeOf, this returns the number 
     * of entries in the cache. For all other caches, this returns the sum of 
     * the sizes of the entries in this cache. 
     * 
     * 返回 缓存中集合的数量  final类型  不允许重写
     */  
    public synchronized final int size()   
        return size;  
      
  
    /** 
     * For caches that do not override @link #sizeOf, this returns the maximum 
     * number of entries in the cache. For all other caches, this returns the 
     * maximum sum of the sizes of the entries in this cache. 
     */  
    public synchronized final int maxSize()   
        return maxSize;  
      
  
    
    /** 
     * Returns the number of times @link #get returned a value that was 
     * already present in the cache. 
     */  
    public synchronized final int hitCount()   
        return hitCount;  
      
  
    
    /** 
     * Returns the number of times @link #get returned null or required a new 
     * value to be created. 
     */  
    public synchronized final int missCount()   
        return missCount;  
      
  
    /** 
     * Returns the number of times @link #create(Object) returned a value. 
     */  
    public synchronized final int createCount()   
        return createCount;  
      
  
    
    /** 
     * Returns the number of times @link #put was called. 
     */  
    public synchronized final int putCount()   
        return putCount;  
      
  
    /** 
     * Returns the number of values that have been evicted. 
     * 返回被回收的数量
     */  
    public synchronized final int evictionCount()   
        return evictionCount;  
      
  
    /** 
     * Returns a copy of the current contents of the cache, ordered from least 
     * recently accessed to most recently accessed. 返回当前cache的副本,从最近最少访问到最多访问
     */  
    public synchronized final Map<K, V> snapshot()   
        return new LinkedHashMap<K, V>(map);  
      
  
    @Override 
    public synchronized final String toString()   
        int accesses = hitCount + missCount;  
        int hitPercent = accesses != 0 ? (100 * hitCount / accesses) : 0;  
        return String.format("LruCache[maxSize=%d,hits=%d,misses=%d,hitRate=%d%%]",  
                maxSize, hitCount, missCount, hitPercent);  
    
    
    
  

以上是关于LRUCache源码解析的主要内容,如果未能解决你的问题,请参考以下文章

Picasso源码解析之Lrucache算法源码解析

LruCache源码解析

Android源码解析——LruCache

Java源码分析Android-LruCache源码分析

Java源码分析Android-LruCache源码分析

Android内存缓存管理LruCache源码解析与示例