LinkedHashMap为何可以实现Lru?

一、Lru的概念
Least recently used (Lru的全称),一般我们翻译为最近做少使用。

二、分析LinkedHashMap
首先看一下它的get方法:

public V get(Object key) {
    Node<K,V> e;
    if ((e = getNode(hash(key), key)) == null)
        return null;
    //这里是关键点,如果accessOrder为true的话,就需要执行afterNodeAccess
    if (accessOrder)
        afterNodeAccess(e);
    return e.value;
}
afterNodeAccess方法比较绕,但是它整体实现的功能是将传入的节点放到链表的尾部。
   void afterNodeAccess(Node<K,V> e) { // move node to last
        LinkedHashMap.Entry<K,V> last;
        if (accessOrder && (last = tail) != e) {
            LinkedHashMap.Entry<K,V> p =
                (LinkedHashMap.Entry<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;
        }
    }

再看它的putVal方法(注意:LinkedHashMap复用了HashMap的put方法):

final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
               boolean evict) {
    //...
    //其他代码都省略了,重点看下面这几行代码
    if (e != null) { // existing mapping for key
        V oldValue = e.value;
        if (!onlyIfAbsent || oldValue == null)
            e.value = value;
        //在上面的代码中我们解释了这段代码的作用,将e节点放到双向链表的尾部
        //所以在LinkedHashMap中的添加的新元素被放到了链表的尾部。
        afterNodeAccess(e);
        return oldValue;
    }
    //...
    //这部分代码在HashMap中是空实现,在LinkedHashMap中有具体实现
    afterNodeInsertion(evict);
}

下面这段代码的作用是删除头节点,通过代码也比较容易看出来:

void afterNodeInsertion(boolean evict) { // possibly remove eldest
    LinkedHashMap.Entry<K,V> first;
    //这里的removeEldestEntry方法是实现lru的关键点
    if (evict && (first = head) != null && removeEldestEntry(first)) {
        K key = first.key;
        removeNode(hash(key), key, null, false, true);
    }
}

经过上面的分析,我们可以做出如下总结:

  1. LinkedHashMap的get操作会将操作的元素移动到链表末尾;
  2. LinkedHashMap的put操作会将新插入的元素移动到链表末尾;
  3. LinkedHashMap的put操作会根据removeEldestEntry(first)判断是否删除头节点。
    上面的三点我们可以看出新插入和新访问的元素都会在链表的尾部,而头部的元素自然就是最近最少访问的节点,所以LinkedHashMap满足Lru的定义,但是还有一个关键点,就是如何设置Lru的大小,上面的代码中已经提示了,我们可以重写removeEldestEntry方法来空值LinkedHashMap的长度:
@Override
protected boolean removeEldestEntry(Map.Entry eldest) {
    return size() > cacheSize;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值