LinkedHashMap源码解析
源码解析对应JDK1.7
JDK1.7源码下载地址:JDK1.7源码下载
JDK 源码注释
Hash table and linked list implementation of the Map interface, with predictable iteration order. This implementation differs from HashMap in that it maintains a doubly-linked list running through all of its entries. This linked list defines the iteration ordering, which is normally the order in which keys were inserted into the map (insertion-order). Note that insertion order is not affected if a key is re-inserted into the map. (A key k is reinserted into a map m if m.put(k, v) is invoked when m.containsKey(k) would return true immediately prior to the invocation.)
This implementation spares its clients from the unspecified, generally chaotic ordering provided by HashMap (and Hashtable), without incurring the increased cost associated with TreeMap. It can be used to produce a copy of a map that has the same order as the original, regardless of the original map's implementation:
void foo(Map m) { Map copy = new LinkedHashMap(m); ... }
This technique is particularly useful if a module takes a map on input, copies it, and later returns results whose order is determined by that of the copy. (Clients generally appreciate having things returned in the same order they were presented.) A special constructor is provided to create a linked hash map whose order of iteration is the order in which its entries were last accessed, from least-recently accessed to most-recently (access-order). This kind of map is well-suited to building LRU caches. Invoking the put or get method results in an access to the corresponding entry (assuming it exists after the invocation completes). The putAll method generates one entry access for each mapping in the specified map, in the order that key-value mappings are provided by the specified map's entry set iterator. No other methods generate entry accesses. In particular, operations on collection-views do not affect the order of iteration of the backing map.
The removeEldestEntry(Map.Entry) method may be overridden to impose a policy for removing stale mappings automatically when new mappings are added to the map.
This class provides all of the optional Map operations, and permits null elements. Like HashMap, it provides constant-time performance for the basic operations (add, contains and remove), assuming the hash function disperses elements properly among the buckets. Performance is likely to be just slightly below that of HashMap, due to the added expense of maintaining the linked list, with one exception: Iteration over the collection-views of a LinkedHashMap requires time proportional to the size of the map, regardless of its capacity. Iteration over a HashMap is likely to be more expensive, requiring time proportional to its capacity.
A linked hash map has two parameters that affect its performance: initial capacity and load factor. They are defined precisely as for HashMap. Note, however, that the penalty for choosing an excessively high value for initial capacity is less severe for this class than for HashMap, as iteration times for this class are unaffected by capacity.
Note that this implementation is not synchronized. If multiple threads access a linked hash map concurrently, and at least one of the threads modifies the map structurally, it must be synchronized externally. This is typically accomplished by synchronizing on some object that naturally encapsulates the map. If no such object exists, the map should be "wrapped" using the Collections.synchronizedMap method. This is best done at creation time, to prevent accidental unsynchronized access to the map:
Map m = Collections.synchronizedMap(new LinkedHashMap(...));
A structural modification is any operation that adds or deletes one or more mappings or, in the case of access-ordered linked hash maps, affects iteration order. In insertion-ordered linked hash maps, merely changing the value associated with a key that is already contained in the map is not a structural modification. In access-ordered linked hash maps, merely querying the map with get is a structural modification.) The iterators returned by the iterator method of the collections returned by all of this class's collection view methods are fail-fast: if the map is structurally modified at any time after the iterator is created, in any way except through the iterator's own remove method, the iterator will throw a ConcurrentModificationException. Thus, in the face of concurrent modification, the iterator fails quickly and cleanly, rather than risking arbitrary, non-deterministic behavior at an undetermined time in the future.
Note that the fail-fast behavior of an iterator cannot be guaranteed as it is, generally speaking, impossible to make any hard guarantees in the presence of unsynchronized concurrent modification. Fail-fast iterators throw ConcurrentModificationException on a best-effort basis. Therefore, it would be wrong to write a program that depended on this exception for its correctness: the fail-fast behavior of iterators should be used only to detect bugs.
This class is a member of the Java Collections Framework.
哈希表和Map接口的链表实现,具有可预测的迭代顺序。这个实现与HashMap的不同之处在于它保持了一个双向链表,它贯穿其所有条目。这个链表定义了迭代排序,这通常是键被插入映射的顺序(插入顺序)。请注意,如果将键重新插入到地图中,则插入顺序不受影响。 (如果m.containsKey(k)在调用之前立即返回true,则调用m.put(k,v)时,将密钥k重新插入到映射m中。)
此实现将客户端从HashMap(和Hashtable)提供的未指定的通常是混乱的排序中排除,而不会增加与TreeMap相关的成本增加。无论原始地图的实现如何,它都可用于生成与原始地图具有相同顺序的地图的副本:
void foo(Map m){ Map copy = new LinkedHashMap(m); ... }
如果一个模块在输入时获取映射,复制该映射并稍后返回其顺序由复制的顺序决定的结果,则此技术特别有用。 (客户通常喜欢以与他们相同的顺序返回东西。) 提供了一个特殊的构造函数来创建一个链接哈希映射,其迭代顺序是最后访问它的条目的顺序,从最近访问到最近(访问顺序)。这种地图非常适合构建LRU缓存。调用put或get方法将导致访问相应的条目(假设它在调用完成后存在)。 putAll方法为指定映射中的每个映射生成一个条目访问,按照由指定映射的条目集迭代器提供键值映射的顺序。没有其他方法生成入口访问。特别是,收集视图的操作不会影响后台映射的迭代顺序。
removeEldestEntry(Map.Entry)方法可能会被覆盖,强制执行一个策略,以便在将新映射添加到地图时自动删除过时映射。
该类提供了所有可选的Map操作,并允许使用null元素。与HashMap一样,假设散列函数能够正确地在元素之间分散元素,它为基本操作(添加,包含和删除)提供了恒定的性能。性能可能略低于HashMap的性能,这是因为维护链接列表的成本增加了,但有一个例外:对LinkedHashMap的集合视图的迭代需要时间与地图大小成比例,而不管其容量。迭代HashMap可能会更加昂贵,需要的时间与其容量成正比。
链接哈希映射有两个影响其性能的参数:初始容量和负载因子。它们的定义与HashMap完全相同。但请注意,对于此类,初始容量选择过高值的惩罚不如HashMap严重,因为此类的迭代时间不受容量影响。
请注意,此实现不同步。如果多个线程同时访问链接的哈希映射,并且至少有一个线程在结构上修改了映射,则它必须在外部同步。这通常是通过在自然封装地图的某个对象上进行同步来完成的。如果不存在这样的对象,则应使用Collections.synchronizedMap方法“映射”该映射。这最好在创建时完成,以防止意外的不同步访问地图:
Map m = Collections.synchronizedMap(new LinkedHashMap(...));
结构修改是添加或删除一个或多个映射的任何操作,或者在访问有序的链接哈希映射的情况下会影响迭代顺序。在插入有序的链接哈希映射中,仅更改与已包含在映射中的键相关的值不是结构修改。在访问有序的链接哈希映射中,仅通过get查询映射就是一种结构修改。) 由所有此类的集合视图方法返回的集合的迭代器方法返回的迭代器都是快速失败的:如果在迭代器创建后的任何时候结构性地修改映射,除了通过迭代器自己的remove方法外,迭代器将抛出ConcurrentModificationException。因此,面对并发修改,迭代器快速而干净地失败,而不是在将来未定的时间冒着任意的,非确定性的行为冒险。
请注意,迭代器的故障快速行为无法得到保证,因为一般来说,在存在非同步并发修改的情况下不可能做出任何硬性保证。 失败快速迭代器尽最大努力抛出ConcurrentModificationException。 因此,编写一个依赖于此异常的程序是正确的:迭代器的快速失败行为应仅用于检测错误。
该类是Java集合框架的成员。 |
首先记住结论:
LinkedHashMap 允许k,v为null。
LinkedHashMap key不允许重复,value允许重复。
LinkedHashMap 是有序的。
LinkedHashMap 非线程安全。
LinkedHashMap基本数据结构
1. LinkedHashMap 可以认为是HashMap + LinkedList;使用HashMap存储数据,使用LinkedList维护插入元素的先后顺序。
2. LinkedHashMap 实现的基本思想是多态。
我们看下LinkedHashMap类的定义:public class LinkedHashMap<K, V> extends HashMap<K, V> implements Map<K, V>
LinkedHashMap继承了HashMap,实现了Map接口;
于是LinkedHashMap是HashMap的子类,LinkedHashMap继承了HashMap中所有非private的方法。
JDK API中的方法
LinkedHashMap中并没有什么操作数据结构的方法,LinkedHashMap操作数据结构和HashMap是一样的,细节上有些不同。
LinkedHashMap 和 HashMap 最大的区别是它们的基本数据结构,我们看下LinkedHashMap的基本数据结构:
/**
* LinkedHashMap entry.
*/
private static class Entry<K, V> extends HashMap.Entry<K, V> {
// These fields comprise the doubly linked list used for iteration.
// 这些字段包括用于迭代的双向链表。
// 这两个字段是LinkedHashMap独有的。
// before,after用于维护Entry插入的先后循序。
Entry<K, V> before, after;
Entry(int hash, K key, V value, HashMap.Entry<K, V> next) {
super(hash, key, value, next);
}
/**
* Removes this entry from the linked list.<br>
* 从链表中删除此条目。
*/
private void remove() {
before.after = after;
after.before = before;
}
/**
* Inserts this entry before the specified existing entry in the
* list.<br>
* 在列表中指定的现有条目之前插入此条目。
*
*/
private void addBefore(Entry<K, V> existingEntry) {
after = existingEntry;
before = existingEntry.before;
before.after = this;
after.before = this;
}
/**
* This method is invoked by the superclass whenever the value of a
* pre-existing entry is read by Map.get or modified by Map.set. If the
* enclosing Map is access-ordered, it moves the entry to the end of the
* list; otherwise, it does nothing.<br>
* 每当Map.get读取预先存在的条目的值或由Map.set修改时,超类就会调用此方法。<br>
* 如果封闭的Map是访问排序的,它将该条目移动到列表的末尾; 否则,它什么都不做。<br>
*/
void recordAccess(HashMap<K, V> m) {
LinkedHashMap<K, V> lm = (LinkedHashMap<K, V>) m;
if (lm.accessOrder) {
lm.modCount++;
remove();
addBefore(lm.header);
}
}
void recordRemoval(HashMap<K, V> m) {
remove();
}
}
罗列下Entry中的属性:
int hash
K key
V value
HashMap.Entry<K, V> next
Entry<K, V> before
Entry<K, V> after
前面四个属性参数是从HashMap.Entry中继承而来;
后两个是LinkedHashMap独有的。
其中next属性是HashMap.Entry中用于维护Entry链表的,不要搞混了。
LinkedHashMap构造方法
LinkedHashMap():
创建一个默认初始容量16和负载因子0.75的空的LinkedHashMap。
LinkedHashMap(int initialCapacity):
创建一个空的LinkedHashMap,指定初始容量和默认负载因子为0.75。
LinkedHashMap(int initialCapacity, float loadFactor):
创建一个空的LinkedHashMap实例,指定初始容量和负载因子。
LinkedHashMap(Map<? extends K, ? extends V> m):
创建一个空的LinkedHashMap,默认初始容量和负载因子,容纳指定集合中的映射。
LinkedHashMap(int initialCapacity, float loadFactor, boolean accessOrder):
创建空的LinkedHahsMap,指定初始容量,负载因子和访问模式。
LinkedHashMap自带参数
/**
* The head of the doubly linked list.<br>
* 双向链表的表头元素
*/
private transient Entry<K, V> header;
/**
* The iteration ordering method for this linked hash map: <tt>true</tt> for
* access-order, <tt>false</tt> for insertion-order.<br>
* true表示按照访问顺序迭代,false时表示按照插入顺序
*
* @serial
*/
private final boolean accessOrder;
put()方法
Demo:
public static void main(String[] args) {
LinkedHashMap<String, String> map = new LinkedHashMap<String, String>();
map.put("a", "a");
}
我们先来看构造方法那边,往下看,他是怎么创建LinkedHashMap对象的。
/**
* Constructs an empty insertion-ordered <tt>LinkedHashMap</tt> instance
* with the default initial capacity (16) and load factor (0.75).<br>
* 创建一个默认初始容量16和负载因子0.75的空的LinkedHashMap
*/
public LinkedHashMap() {
super();
accessOrder = false;
}
我们去super()方法里面去看,因为继承了HashMap,所以super()方法是HashMap中的默认构造方法
/**
* Constructs an empty <tt>HashMap</tt> with the default initial capacity
* (16) and the default load factor (0.75).
*/
public HashMap() {
this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR);
}
再通过this方法往下看:
/**
* 指定容量大小和加载因子的构造函数
*
* @param initialCapacity
* @param loadFactor
*/
public HashMap(int initialCapacity, float loadFactor) {
// 参数校验
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal initial capacity: " + initialCapacity);
// 指定容量是否超出最大容量限制校验,如果超出,就用最大容量
if (initialCapacity > MAXIMUM_CAPACITY)
initialCapacity = MAXIMUM_CAPACITY;
// 校验加载因子,判断参数是否是"非数字"的值
if (loadFactor <= 0 || Float.isNaN(loadFactor))
throw new IllegalArgumentException("Illegal load factor: " + loadFactor);
// Find a power of 2 >= initialCapacity
// 找出"大于initialCapacity(指定容量)的最小2的幂"
int capacity = 1;
while (capacity < initialCapacity)
capacity <<= 1;
// 加载因子赋值
this.loadFactor = loadFactor;
// 设置HashMap的阀值,当HashMap中存储的数据量达到threshold时,需要扩容
// Math.min():选择一个小的参数
threshold = (int) Math.min(capacity * loadFactor, MAXIMUM_CAPACITY + 1);
// 创建指定长度的table数组(entry)
table = new Entry[capacity];
useAltHashing = sun.misc.VM.isBooted() && (capacity >= Holder.ALTERNATIVE_HASHING_THRESHOLD);
init();
}
很眼熟吧...HashMap中的构造方法。
注意最后有一个init()方法,在HashMap中是没有做任何处理的,我们使用Eclipse的快捷键(Control + t)点击init()方法。
你看,init()方法,被LinkedHashMap重写了,这就是多态的体现..
我们看看重写了什么...
/**
* Called by superclass constructors and pseudoconstructors (clone,
* readObject) before any entries are inserted into the map. Initializes the
* chain.<br>
* 在任何条目插入映射之前,由超类构造函数和伪构造函数(clone,readObject)调用。 初始化链。
*/
@Override
void init() {
header = new Entry<>(-1, null, null, null);
header.before = header.after = header;
}
这个init()方法是LinkedHashMap中的,init()方法之后创建了一个空的LinkedHashMap
我们接着上面Demo往下看,接着就是put元素了
我们在Eclipse按住control,然后单机put(),会发现,进入了HashMap的put()方法
public V put(K key, V value) {
// 如果key为空,将null存放在table[0]第一个位置,这就是HashMap允许存null的原因
if (key == null)
return putForNullKey(value);
// 计算key的hash值
int hash = hash(key);
// 根据hash码和数组长度,计算table数组下标
int i = indexFor(hash, table.length);
// 从i处开始迭代entry链表,找到key保存的位置
for (Entry<K, V> e = table[i]; e != null; e = e.next) {
Object k;
// 判断该链条上是否有hash值相同的(key相同)
// 若存在key相同,直接覆盖value,返回旧的value
if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
V oldValue = e.value;// 取出旧值
e.value = value;// 赋新值
e.recordAccess(this);
return oldValue;// 返回旧值
}
}
// 修改次数+1
modCount++;
// i处没有entry链表(该位置为空),将key,value添加至i处
addEntry(hash, key, value, i);
return null;
}
看到这个put()方法里面最后有个addEntry()方法吗?
这个方法也是个多态...control + t 可以看到LinkeHashMap实现了它。
我们去LinkedHashMap里面看看..
/**
* This override alters behavior of superclass put method. It causes newly
* allocated entry to get inserted at the end of the linked list and removes
* the eldest entry if appropriate.<br>
*
* 这个覆盖改变了超类put方法的行为。<br>
* 它导致新分配的条目在链接列表的末尾插入,并在适当的情况下删除最老的条目。<br>
*
*/
void addEntry(int hash, K key, V value, int bucketIndex) {
super.addEntry(hash, key, value, bucketIndex);
// Remove eldest entry if instructed
Entry<K, V> eldest = header.after;
if (removeEldestEntry(eldest)) {
removeEntryForKey(eldest.key);
}
}
if判断里面的语句暂时不看。
我们跟着super.addEntry(hash, key, value, bucketIndex);继续往下看...
返回到了HashMap.Entry中的addEntry中:
void addEntry(int hash, K key, V value, int bucketIndex) {
// 首先判断是否需要扩容
// 'hashMap的大小' 大于等于 '阀值(加载因子*容量)' && table数组对应下标位置有数据
if ((size >= threshold) && (null != table[bucketIndex])) {
// 容量扩大两倍
resize(2 * table.length);
// key为null,hash取0
// key不为null,根据key计算hash
hash = (null != key) ? hash(key) : 0;
// 重新计算哈希码的索引
bucketIndex = indexFor(hash, table.length);
}
// 创建entry
createEntry(hash, key, value, bucketIndex);
}
最后一个方法:createEntry()
猜到了吧,LinkedHashMap重写了它
跟下去看看..
/**
* This override differs from addEntry in that it doesn't resize the table
* or remove the eldest entry.<br>
* 此覆盖与addEntry不同之处在于它不会调整表的大小或删除最老的条目。
*/
void createEntry(int hash, K key, V value, int bucketIndex) {
HashMap.Entry<K, V> old = table[bucketIndex];
Entry<K, V> e = new Entry<>(hash, key, value, old);
table[bucketIndex] = e;
e.addBefore(header);
size++;
}
前两行代码不用解释吧,看明白HashMap就知道了...
我们看看e.addBefore(header)方法
/**
* Inserts this entry before the specified existing entry in the
* list.<br>
* 在列表中指定的现有条目之前插入此条目。
*
*/
// existingEntry表示的是header
private void addBefore(Entry<K, V> existingEntry) {
// 新增entry的后一个节点(after)=header地址。
after = existingEntry;
// 新增entry的前一个节点(before)是header的before地址
before = existingEntry.before;
//header的after此时等于新增元素entry
before.after = this;
//header的before此时等于新增元素entry
after.before = this;
}
(感谢原作者的图例)
上面这段代码我看了半天才看明白,后来看了这张图才明白..(建议看不明白的,多看两遍,对着图)
至此,header与新增entry的双向链表就形成了。
get()方法
源码:
/**
* Returns the value to which the specified key is mapped, or {@code null}
* if this map contains no mapping for the key.<br>
* 返回指定键映射到的值,如果此映射不包含该键的映射,则返回{@code null}。
*
* <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) {
// HashMap中的方法,根据key获取对应value
Entry<K, V> e = (Entry<K, V>) getEntry(key);
// 为空返回null
if (e == null)
return null;
e.recordAccess(this);
return e.value;
}
上面几个方法很好理解吧..
看下e.recordAccess(this);
recordAccess,顾名思义,记录访问,就是说你这次访问了双向链表,将它记录下来,怎么记录?
将访问的对象(Entry)移到队列尾部。在HashMap中没有实现,LinkedHashMap对其进行了实现。
/**
* This method is invoked by the superclass whenever the value of a
* pre-existing entry is read by Map.get or modified by Map.set. If the
* enclosing Map is access-ordered, it moves the entry to the end of the
* list; otherwise, it does nothing.<br>
* 每当Map.get读取预先存在的条目的值或由Map.set修改时,超类就会调用此方法。<br>
* 如果封闭的Map是访问排序的,它将该条目移动到列表的末尾; 否则,它什么都不做。<br>
*/
void recordAccess(HashMap<K, V> m) {
LinkedHashMap<K, V> lm = (LinkedHashMap<K, V>) m;
if (lm.accessOrder) {
lm.modCount++;
remove();
addBefore(lm.header);
}
}
/**
* Removes this entry from the linked list.<br>
* 从链表中删除此条目。
*/
private void remove() {
before.after = after;
after.before = before;
}
/**
* Inserts this entry before the specified existing entry in the
* list.<br>
* 在列表中指定的现有条目之前插入此条目。
*
*/
// existingEntry表示的是header
private void addBefore(Entry<K, V> existingEntry) {
// 新增entry的后一个节点(after)=header地址。
after = existingEntry;
// 新增entry的前一个节点(before)是header的before地址
before = existingEntry.before;
// header的after此时等于新增元素entry
before.after = this;
// header的before此时等于新增元素entry
after.before = this;
}
上面代码看出每次recordAccess的时候做了两件事情:
a. 把待移动的Entry前后Entry想连
b. 把待移动的Entry移动到尾部
这些都是基于accessOrder=true的情况下,看下图(感谢原作者的图)
上面这两个方法捋明白了,其他的方法也就懂了..
下面我们看看LRU算法
LinkedHashMap实现LRU算法缓存
LRU算法是什么?
LRU是" Least Recently Used "的缩写,翻译过来是"最近最少使用"。
也就是说LRU算法会将最近最少使用的缓存移除,让给最新使用的缓存;而往往经常读取的,也就是读取次数最多的。
所以利用好LRU缓存,提高对热点数据的缓存效率及内存使用率。
当缓存满了,会优先淘汰那些最近最不常访问的数据。
举个栗子:
数据a,一天前访问过;数据b,两天前访问过;当缓存满了,优先淘汰数据b。
LRU算法的实现
LRU实现需要那几步?
a. 限制缓存大小
b. 查询出最近最少使用的缓存
c. 给最近最少使用的缓存做一个标识
就如下图:(感谢原作者的图)
我们看下LinkedHashMap有参构造函数方法
/**
* Constructs an empty <tt>LinkedHashMap</tt> instance with the specified
* initial capacity, load factor and ordering mode.<br>
* 创建空的LinkedHahsMap,指定初始容量,负载因子和访问模式
*
* @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;
}
最后一个参数accessOrder:
a. false:所有的entry按照插入的顺序排列
b. true:所有的entry按照访问顺序排列
解释下第二个:
如果链表中有1 2 3个entry元素,如果你访问了1,那就把1移动到链表尾部,就是 2 3 1 。
每次访问都是将访问的数据移到链表尾部;
于是每次要淘汰数据的时候,链表头部的那个数据不就是最不常访问的那个数据了嘛...
换句话说,双向链表头部的数据就是要淘汰的数据。
简单实现
当缓存中的数据超出阀值,将最老的数据删除
/**
* 基于LinkedHashMap实现LRU算法
*
* @author CYX
*
* @param <K>
* @param <V>
*/
public class LRULinkedashMap<K, V> extends LinkedHashMap<K, V> {
private static final long serialVersionUID = 1L;
// 定义缓存容量
private int capacity;
public LRULinkedashMap(int capacity) {
// 创建LinkedHashMap,使用默认参数
super(16, 0.75F);
// 缓存最大容量
this.capacity = capacity;
}
/**
* 实现LRU算法的关键方法,如果LinkedHashMap中的元素大于缓存最大容量,则删除表的顶端元素
*/
@Override
protected boolean removeEldestEntry(java.util.Map.Entry<K, V> eldest) {
System.out.println("==== " + eldest.getKey() + " , " + eldest.getValue());
return size() > capacity;
}
}
public class TestLRU {
public static void main(String[] args) {
// 创建LRULinkedashMap对象,并指定缓存容量最大为4
Map<Integer, Integer> map = new LRULinkedashMap<Integer, Integer>(4);
for (int i = 0; i < 10; i++) {
map.put(i, i + 1);
}
iter(map);
}
private static void iter(Map<Integer, Integer> map) {
for (Entry<Integer, Integer> iterator : map.entrySet()) {
System.out.println(iterator.getKey() + " , " + iterator.getValue());
}
}
}
输出结果:
==== 0 , 1
==== 0 , 1
==== 0 , 1
==== 0 , 1
==== 0 , 1
==== 1 , 2
==== 2 , 3
==== 3 , 4
==== 4 , 5
==== 5 , 6
6 , 7
7 , 8
8 , 9
9 , 10
解释下:
main()方法中创建LRULinkedashMap对象,设置阀值为4,然后循环放入key为0~9的数据。
启用LRU模式的LinkedHashMap会在每次有新元素加入的时候,判断当前存储元素数量是否超出缓存上线,
也就是执行removeEldestEntry()方法,通过输出结果,我们看到它将6之前的元素全部删除了。
参考资料:
http://www.cnblogs.com/xrq730/p/5052323.html
http://uule.iteye.com/blog/1522291
https://tonydeng.github.io/2015/07/16/linkedhashmap-based-lrucache-implementation/