LruCache及LinkedHashMap原理分析

 LRU(Least Recently Used)  最近最少使用算法,或命名为最近最久末被使用算法。  意思就是到当前时间为止最少使用算法,它的核心思想就是会优先淘汰那些近期最少使用的缓存对象。

一、LRU 要解决以下几个问题:

1、LRU缓存 需要 提供 添加、访问、删除 的方法

2、在第1步的几个方法中,需要根据当前LRU的总容量,以及本次操作的元素的大小,来决定是否需要移除最少使用的元素。

3、如何确定谁是最少使用的元素

andrid-27的源码中就自带了一个 LRU的实现: android.util.LruCache<K, V>  

它内部采用的数据结果是 LinkedHashMap<K, V> , 这是个 双向链表,

构造方法:

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

二、为什么要使用LinkedHashMap?

当 accessOrder 为 true 时,这个集合的元素顺序就会是访问顺序,也就是访问了之后就会将这个元素放到集合的最后面。这样近期被使用的过元素总是在链表的末尾,那么 最久末被使用过的元素都保存在链表表头, 当缓存容量超过最大容量限制时,只需要直接移除表头的元素就行,大大提高了效率。

LinkedHashMap 的 get 方法如下,当 accessOrder 为true时,它会将当前访问的元素 移动到链表末尾, 链表的操作就不多说了,基本的数据结构知识还是要有的,大家自己看源码.

LinkedHashMap.java 源码

public V get(Object key) {
    Node<K,V> e;
    if ((e = getNode(hash(key), key)) == null)
        return null;
    if (accessOrder)
        afterNodeAccess(e);
    return e.value;
}

afterNodeAccess  源码的方法的命名也很好,翻译过来就是 “访问完节点之后” 的动作,那自然就是把访问的节点移动到链表末尾。

void afterNodeAccess(Node<K,V> e) { // move node to last
        LinkedHashMapEntry<K,V> last;
        if (accessOrder && (last = tail) != e) {
            LinkedHashMapEntry<K,V> p =
                (LinkedHashMapEntry<K,V>)e, b = p.before, a = p.after;
            p.after = null;
            if (b == null)
                head = a;
            else
                b.after = a;
            if (a != null)
                a.before = b;
            else
                last = b;
            if (last == null)
                head = p;
            else {
                p.before = last;
                last.after = p;
            }
            tail = p;
            ++modCount;
        }
    }

三、为什么不使用其它数据结构?

如果使用其它普通数据结构,如ArrayList,那么就需要自己记录每一个元素的访问时间,每次操作时都来查找 之前记录的访问时间,找到时间最早的记录来删除, 这样就降低了处理的效率。

四、如何确定单个元素的大小?

在sdk自带的 LruCache中 ,sizeOf 的计算方法 默认返回了 1,   因为它的key和value 都是泛型的, 它并不知道具体的value 类型,那就无法知道 value所占用的 size的大小了。  我们业务层要使用这个内存LRU缓存时,需要继承sdk中的 LruCache 并重写 sizeOf方法。  

/**
     * Returns the size of the entry for {@code key} and {@code value} in
     * user-defined units.  The default implementation returns 1 so that size
     * is the number of entries and max size is the maximum number of entries.
     *
     * <p>An entry's size must not change while it is in the cache.
     */
    protected int sizeOf(K key, V value) {
        return 1;
    }

五、应用场景

例如实现 图片内存Lru缓存功能时, ImageLruCaceh extend LruCaceh<String, Bitmap>  ,     重写sizeOf  返回单个图片所占用的 内存字节大小。

new LruCache<String, Bitmap>(cacheSize) {
            @Override
            protected int sizeOf(String key, Bitmap value) {
                return value.getByteCount();
            }
        };

六、LinkedHashMap

 LinkedHashMap增加了时间和空间上的开销,但是通过维护一个运行于所有条目的双向链表,LinkedHashMap保证了元素迭代的顺序该迭代顺序可以是插入顺序或者是访问顺序。

关  注  点结      论
LinkedHashMap是否允许空Key和Value都允许空
LinkedHashMap是否允许重复数据Key重复会覆盖、Value允许重复
LinkedHashMap是否有序有序
LinkedHashMap是否线程安全非线程安全

 

 

 

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值