LruCache源码

1.构造方法

/**
     * @param maxSize for caches that do not override {@link #sizeOf}, this is
     *     the maximum number of entries in the cache. For all other caches,
     *     this is the maximum sum of the sizes of the entries in this cache.
     */
    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,所以它是用LinkedHashMap来保存缓存数据,再看看LinkedHashMap的构造方法做了什么。

public LinkedHashMap(
            int initialCapacity, float loadFactor, boolean accessOrder) {
        super(initialCapacity, loadFactor);
        init();
        this.accessOrder = accessOrder;
    }

第一个参数是初始大小,第二个参数是负载因子,默认是0.75f,就是当HashMap内容超过0.75后会主动扩容,第三个参数是控制排序,如果为true,那么LinkedHashMap就是按照最常访问排序,为false则是按照插入时间排序。

if (accessOrder)
                makeTail((LinkedEntry<K, V>) e);

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++;                             
}    

LinkedHashMap是一个双向循环链表,这段代码第一步就是将传进去的e节点先剥离出来,然后将其放到剥离之后的链表的尾巴上:

这里写图片描述

2.get()方法

/**                                                                          
 * Returns the value for {@code key} if it exists in the cache or can be     
 * created by {@code #create}. If a value was returned, it is moved to the   
 * head of the queue. This returns null if a value is not cached and cannot  
 * be created.                                                               
 */                                                                          
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                    
            map.put(key, mapValue);                                          
        } else {                                                             
            size += safeSizeOf(key, createdValue);                           
        }                                                                    
    }                                                                        

    if (mapValue != null) {                                                  
        entryRemoved(false, key, createdValue, mapValue);                    
        return mapValue;                                                     
    } else {                                                                 
        trimToSize(maxSize);                                                 
        return createdValue;                                                 
    }                                                                        
}                        

首先判断key的有效性,然后进入一个同步块中,因为LinkedHashMap不是线程安全的,所以要在同步块里处理数据,如果拿到目标,就直接返回目标,否则进行下一步,通过create()方法生成一个目标,但是create()默认返回null,需要自己重写:

protected V create(K key) {
        return null;
    }

如果没重写这个方法,会在判断createValue时返回null,也就没下面的事情了,如果重写了这个方法,就要更新数据,再次进入同步块,这里做了两次put操作,是因为create()方法没有在同步块中,有可能当create()了一个key的值之后这个key对应的值已经并不是null了(有可能此时key已经被put了值进去)。

第二个同步块里面在成功放入create的mapValue之后进行了一个trimToSize()的操作,很显然,这是在控制LinkedHashMap的大小,剔除最不常使用的变量,它的具体写法是这样的:

/**                                                                           
 * Remove the eldest entries until the total of remaining entries is at or    
 * below the requested size.                                                  
 *                                                                            
 * @param maxSize the maximum size of the cache before returning. May be -1   
 *            to evict even 0-sized elements.                                 
 */                                                                           
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;                                                        
            }                                                                 

            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);                                 
    }                                                                         
}  

大概意思就是如果超出了限定大小,就要把最旧的数据移除,直到符合要求。

2.put()方法

/**                                                                         
 * Caches {@code value} for {@code key}. The value is moved to the head of  
 * the queue.                                                               
 *                                                                          
 * @return the previous value mapped by {@code key}.                        
 */                                                                         
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) {                                             
            size -= safeSizeOf(key, previous);                              
        }                                                                   
    }                                                                       

    if (previous != null) {                                                 
        entryRemoved(false, key, previous, value);                          
    }                                                                       

    trimToSize(maxSize);                                                    
    return previous;                                                        
}        

put()也是类似的检查合法性,放入数据,调整大小的过程。

参考博文:
LruCache源码解析

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值