LinkedHashMap

一、基础概念

下图很好地还原了LinkedHashMap的原貌:HashMap和双向链表的密切配合和分工合作造就了LinkedHashMap。特别需要注意的是,next用于维护HashMap各个桶中的Entry链,before、after用于维护LinkedHashMap的双向链表,虽然它们的作用对象都是Entry,但是各自分离,是两码事儿。

这里写图片描述

LinkedHashMap的继承结构如图:

这里写图片描述

HashMap与LinkedHashMap的Entry结构示意图如下图所示

这里写图片描述

1.遍历
LinkedHashMap进行遍历时,迭代的是LinkedList这个双向链表来进行迭代输出,这样就保证了元素的有序性

private abstract class LinkedHashIterator<T> implements Iterator<T> {
    Entry<K,V> nextEntry    = header.after;
    Entry<K,V> lastReturned = null;

    /**
     * The modCount value that the iterator believes that the backing
     * List should have.  If this expectation is violated, the iterator
     * has detected concurrent modification.
     */
    int expectedModCount = modCount;

    public boolean hasNext() {         // 根据双向列表判断 
            return nextEntry != header;
    }

    public void remove() {
        if (lastReturned == null)
        throw new IllegalStateException();
        if (modCount != expectedModCount)
        throw new ConcurrentModificationException();

            LinkedHashMap.this.remove(lastReturned.key);
            lastReturned = null;
            expectedModCount = modCount;
    }

    Entry<K,V> nextEntry() {        // 迭代输出双向链表各节点
        if (modCount != expectedModCount)
        throw new ConcurrentModificationException();
            if (nextEntry == header)
                throw new NoSuchElementException();

            Entry<K,V> e = lastReturned = nextEntry;
            nextEntry = e.after;
            return e;
    }
}

// Key 迭代器,KeySet
private class KeyIterator extends LinkedHashIterator<K> {   
    public K next() { return nextEntry().getKey(); }
}

   // Value 迭代器,Values(Collection)
private class ValueIterator extends LinkedHashIterator<V> {
    public V next() { return nextEntry().value; }
}

// Entry 迭代器,EntrySet
private class EntryIterator extends LinkedHashIterator<Map.Entry<K,V>> {
    public Map.Entry<K,V> next() { return nextEntry(); }
}

2.读取

	
    /**
     * Returns the value to which the specified key is mapped,
     * or {@code null} if this map contains no mapping for the key.
     *
     * <p>More formally, if this map contains a mapping from a key
     * {@code k} to a value {@code v} such that {@code (key==null ? k==null :
     * key.equals(k))}, then this method returns {@code v}; otherwise
     * it returns {@code null}.  (There can be at most one such mapping.)
     *
     * <p>A return value of {@code null} does not <i>necessarily</i>
     * indicate that the map contains no mapping for the key; it's also
     * possible that the map explicitly maps the key to {@code null}.
     * The {@link #containsKey containsKey} operation may be used to
     * distinguish these two cases.
     */
    public V get(Object key) {
        // 根据key获取对应的Entry,若没有这样的Entry,则返回null
        Entry<K,V> e = (Entry<K,V>)getEntry(key); 
        if (e == null)      // 若不存在这样的Entry,直接返回
            return null;
        e.recordAccess(this);
        return e.value;
    }

/**
     * Returns the entry associated with the specified key in the
     * HashMap.  Returns null if the HashMap contains no mapping
     * for the key.
     * 
     * HashMap 中的方法
     *     
     */
    final Entry<K,V> getEntry(Object key) {
        if (size == 0) {
            return null;
        }

        int hash = (key == null) ? 0 : hash(key);
        for (Entry<K,V> e = table[indexFor(hash, table.length)];
             e != null;
             e = e.next) {
            Object k;
            if (e.hash == hash &&
                ((k = e.key) == key || (key != null && key.equals(k))))
                return e;
        }
        return null;
    }



在LinkedHashMap的get方法中,通过HashMap中的getEntry方法获取Entry对象。即用到的是HashMap,不会用到before和after节点。
注意这里的recordAccess方法,如果链表中元素的排序规则是按照插入的先后顺序排序的话,该方法什么也不做;如果链表中元素的排序规则是按照访问的先后顺序排序的话,则将e移到链表的末尾处

3.插入
LinkedHashMap的插入就是HashMap+LinkedList的实现方式,以HashMap维护数据结构,以LinkList的方式维护数据插入顺序。即LinkedHashMap在插入时保证顺序主要是通过before和after节点的改变。

详细过程请参考
图解集合6:LinkedHashMap:http://www.cnblogs.com/xrq730/p/5052323.html

4.删除
LinkedHashMap的删除同时维护HashMap+LinkedList,HashMap维护数据结构,LinkList维护数据的原先顺序,即LinkedHashMap在删除时会打断before和after节点,然后重组before和after所指引的位置来保护数据的顺序。

详细过程请参考
图解集合6:LinkedHashMap:http://www.cnblogs.com/xrq730/p/5052323.html

二、LRU算法

2.1 背景

用户系统架构:
在这里插入图片描述

用户信息当然是存在数据库里。但是由于我们对用户系统的性能要求比较高,显然不能每一次请求都去查询数据库。
所以,小灰在内存中创建了一个哈希表作为缓存,每次查找一个用户的时候先在哈希表中查询,以此提高访问性能。

此种设计的弊端:
当用户数量越来越大时,很容易将缓存中间件冲爆,当缓存中间件发送内存溢出等出现岩机,请求将到达数据库,即发送缓存雪崩

2.2 LRU

LRU(Least recently used):最近最少使用,即最近最少被使用的数据会被优先清除。

设计思路:

1.插入的数据自动放到链表的最尾端。
2.访问的数据,会打断该数据原链表的位置,重新排到链表的尾端。
3.当内存不够时,优先清除链表头部的数据。

详细流程:

让我们以用户信息的需求为例,来演示一下LRU算法的基本思路:

1.假设我们使用哈希链表来缓存用户信息,目前缓存了4个用户,这4个用户是按照时间顺序依次从链表右端插入的。

在这里插入图片描述

2.此时,业务方访问用户5,由于哈希链表中没有用户5的数据,我们从数据库中读取出来,插入到缓存当中。这时候,链表中最右端是最新访问到的用户5,最左端是最近最少访问的用户1。
在这里插入图片描述
3.接下来,业务方访问用户2,哈希链表中存在用户2的数据,我们怎么做呢?我们把用户2从它的前驱节点和后继节点之间移除,重新插入到链表最右端。这时候,链表中最右端变成了最新访问到的用户2,最左端仍然是最近最少访问的用户1。

在这里插入图片描述
在这里插入图片描述
4.接下来,业务方请求修改用户4的信息。同样道理,我们把用户4从原来的位置移动到链表最右侧,并把用户信息的值更新。这时候,链表中最右端是最新访问到的用户4,最左端仍然是最近最少访问的用户1。
在这里插入图片描述
在这里插入图片描述
5.后来业务方换口味了,访问用户6,用户6在缓存里没有,需要插入到哈希链表。假设这时候缓存容量已经达到上限,必须先删除最近最少访问的数据,那么位于哈希链表最左端的用户1就会被删除掉,然后再把用户6插入到最右端。
在这里插入图片描述
在这里插入图片描述

2.3 LinkedHashMap实现LRU算法原理

使用LinkedHashMap实现LRU的必要前提是将accessOrder标志位设为true以便开启按访问顺序排序的模式。LinkedHashMap默认为false。

实现原理

我们知道,当accessOrder标志位为true时,表示双向链表中的元素按照访问的先后顺序排列,可以看到,虽然Entry插入链表的顺序依然是按照其put到LinkedHashMap中的顺序,但put和get方法均有调用recordAccess方法(put方法在key相同时会调用)。
 
1.recordAccess方法判断accessOrder是否为true,如果是,则将当前访问的Entry(put进来的Entry或get出来的Entry)移到双向链表的尾部(key不相同时,put新Entry时,会调用addEntry,它会调用createEntry,该方法同样将新插入的元素放入到双向链表的尾部,既符合插入的先后顺序,又符合访问的先后顺序,因为这时该Entry也被访问了);

2.当标志位accessOrder的值为false时,表示双向链表中的元素按照Entry插入LinkedHashMap到中的先后顺序排序,即每次put到LinkedHashMap中的Entry都放在双向链表的尾部,这样遍历双向链表时,Entry的输出顺序便和插入的顺序一致,这也是默认的双向链表的存储顺序。因此,当标志位accessOrder的值为false时,虽然也会调用recordAccess方法,但不做任何操作。

注意:使用lru算法当容量不足时,会从头节点开始移除数据。

参考资料

1.Map 综述(二):彻头彻尾理解 LinkedHashMap
http://blog.csdn.net/justloveyou_/article/details/71713781
2.漫画:什么是LRU(Least Recently Used)算法?https://blog.csdn.net/wydyd110/article/details/84023688

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值