LinkedHashMap初识

1.LRU算法

LRU算法的设计原则是:如果一个数据在最近一段时间没有被访问到,那么在将来它被访问的可能性也很小。也就是说,当限定的空间已存满数据时,应当把最久没有被访问到的数据淘汰。
1.使用LinkedHashMap实现
LinkedHashMap底层就是用的HashMap加双链表实现的,而且本身已经实现了按照访问顺序的存储。此外,LinkedHashMap中本身就实现了一个方法removeEldestEntry用于判断是否需要移除最不常读取的数,方法默认是直接返回false,不会移除元素,所以需要重写该方法。即当缓存满后就移除最不常用的数。
当存在热点数据时,LRU的效率很好,但偶发性的、周期性的批量操作会导致LRU命中率急剧下降,缓存污染情况比较严重。
2.LRU-K
LRU-K中的K代表最近使用的次数,因此LRU可以认为是LRU-1。LRU-K的主要目的是为了解决LRU算法“缓存污染”的问题,其核心思想是将“最近使用过1次”的判断标准扩展为“最近使用过K次”。
相比LRU,LRU-K需要多维护一个队列,用于记录所有缓存数据被访问的历史。只有当数据的访问次数达到K次的时候,才将数据放入缓存。当需要淘汰数据时,LRU-K会淘汰第K次访问时间距当前时间最大的数据。
3.two queue
Two queues(以下使用2Q代替)算法类似于LRU-2,不同点在于2Q将LRU-2算法中的访问历史队列(注意这不是缓存数据的)改为一个FIFO缓存队列,即:2Q算法有两个缓存队列,一个是FIFO队列,一个是LRU队列。当数据第一次访问时,2Q算法将数据缓存在FIFO队列里面,当数据第二次被访问时,则将数据从FIFO队列移到LRU队列里面,两个队列各自按照自己的方法淘汰数据。
3.Multi Queue(MQ)
MQ算法根据访问频率将数据划分为多个队列,不同的队列具有不同的访问优先级,其核心思想是:优先缓存访问次数多的数据。
在这里插入图片描述

2.LinkedHashMap

/**
 * Constructs an empty <tt>LinkedHashMap</tt> instance with the
 * specified initial capacity, load factor and ordering mode.
 *
 * @param  initialCapacity the initial capacity
 * @param  loadFactor      the load factor
 * @param  accessOrder     the ordering mode - <tt>true</tt> for
 *         access-order, <tt>false</tt> for insertion-order
 * @throws IllegalArgumentException if the initial capacity is negative
 *         or the load factor is nonpositive
 */
public LinkedHashMap(int initialCapacity,
                     float loadFactor,
                     boolean accessOrder) {
    super(initialCapacity, loadFactor);
    this.accessOrder = accessOrder;
}

参数说明:
1.initialCapacity 初始容量大小,使用无参构造方法时,此值默认是16
2.loadFactor 加载因子,使用无参构造方法时,此值默认是 0.75
3.faccessOrder false: 基于插入顺序 true: 基于访问顺序
我们常说linkedHashMap是数组+链表(双向循环链表)

/**
 * HashMap.Node subclass for normal LinkedHashMap entries.
 */
static class Entry<K,V> extends HashMap.Node<K,V> {
    Entry<K,V> before, after;
    Entry(int hash, K key, V value, Node<K,V> next) {
        super(hash, key, value, next);
    }
}

这是LinkedHashMap的Entry,相比其父类HashMap而言多了befor和after,所以这里就有了4个成员变量:
·hash值(这个容易被忽略)
·key 和 value 两个变量
·用于维护哈希表的 next
·用于维护双向循环链表的 before 和 after

这里找了一个图片,可以作为参考加强理解
在这里插入图片描述
扩容
扩容时机:当map中包含的entry数量大于等于容量x加载因子即initialCapacity x loadFactor 。
扩容机制:2倍!当size大于等于threshold的时候,并不一定会触发扩容机制,但是会很可能就触发扩容机制,只要有一个新建的Entry出现哈希冲突,则立刻resize。
AccessOrder
accessOrder默认为faslse。这里需要介绍一下这个布尔值,它是双向链表中元素排序规则的标志位。
(1)accessOrder若为false,遍历双向链表时,是按照插入顺序排序。
(2)accessOrder若为true,表示双向链表中的元素按照访问的先后顺序排列,最先遍历到(链表头)的是最近最少使用的元素。
只要是put进来的新元素,不管accessOrder标志位是什么,均将新元素放到双链表尾部,并且可以在需要实现Lru算法时时覆写removeEldestEntry方法,剔除最近最少使用的节点。还有两种情况,get获取元素、还有put进Key已经存在的元素,即调用recordAccess的这两种情况下,这个时候标志位就起作用了,accessOrder为fasle时,什么也不做,也就是说当我们放入已经存在Key的键值对或get操作时,它在双链表中的位置是不会变的。accessOrder设置为true时,上述两种情况会将相关元素放置到双链表的尾部。在缓存的角度来看,这就是所谓的“脏数据”,即最近被访问过的数据,因此在需要清理内存时(添加进新元素时),就可以将双链表头节点(空节点)后面那个节点剔除。
注:
为什么是2倍,是因为性能,具体的请自行查证。
linkedHashMap是线程不安全的,多线程可以参考并发工具包下的ConcurrentHashMap。

引用

https://blog.csdn.net/elricboa/article/details/78847305
https://blog.csdn.net/lansefanggezi123/article/details/79587858
https://blog.csdn.net/jiankeufo/article/details/73332772
https://blog.csdn.net/cds86333774/article/details/50946990
https://blog.csdn.net/u012961566/article/details/72963157
https://blog.csdn.net/u014532901/article/details/78936283
https://www.cnblogs.com/xiaoxi/p/6170590.html

猜你喜欢

转载自blog.csdn.net/weixin_43189126/article/details/85328392