LinkedHashMap原理分析
一.实现
LinkedHashMap继承HashMap
数据结构:HashMapEntry(key,value)--》LinkedHashMapEntry(key,value+before指针和after指针(双向链表))
1. 构造函数LinkedHashMap
public LinkedHashMap (
int initialCapacity, float loadFactor, boolean accessOrder) {
super (initialCapacity, loadFactor);
init();
this .accessOrder = accessOrder;
}
super (initialCapacity, loadFactor) {
...
}
@Override
void init() {
header = new LinkedEntry<K, V>();
}
static class LinkedEntry<K, V> extends HashMapEntry<K, V> {
LinkedEntry<K, V> nxt;
LinkedEntry<K, V> prv;
/** Create the header entry */
LinkedEntry() {
super (null , null , 0 , null );
nxt = prv = this ;
}
/** Create a normal entry */
LinkedEntry(K key, V value, int hash, HashMapEntry<K, V> next,
LinkedEntry<K, V> nxt, LinkedEntry<K, V> prv) {
super (key, value, hash, next);
this .nxt = nxt;
this .prv = prv;
}
}
2. put函数--同HashMap的put函数
@Override
void preModify(HashMapEntry<K, V> e) {
if (accessOrder) {
makeTail((LinkedEntry<K, V>) e);
}
}
/**
* Relinks the given entry to the tail of the list. Under access ordering,
* this method is invoked whenever the value of a pre-existing entry is
* read by Map.get or modified by Map.put.
*/
private void makeTail (LinkedEntry<K, V> e) {
e.prv.nxt = e.nxt;
e.nxt.prv = e.prv;
LinkedEntry<K, V> header = this .header;
LinkedEntry<K, V> oldTail = header.prv;
e.nxt = header;
e.prv = oldTail;
oldTail.nxt = header.prv = e;
modCount++;
}
@Override
void addNewEntry(K key, V value, int hash, int index) {
LinkedEntry<K, V> header = this .header;
LinkedEntry<K, V> eldest = header.nxt;
if (eldest != header && removeEldestEntry(eldest)) {
remove(eldest.key);
}
LinkedEntry<K, V> oldTail = header.prv;
LinkedEntry<K, V> newTail = new LinkedEntry<K,V>(key, value, hash, table[index], header, oldTail);
table[index] = oldTail.nxt = header.prv = newTail;
}
3. get函数
@Override
public V get (Object key) {
...
int hash = secondaryHash(key);
HashMapEntry<K, V>[] tab = table;
for (HashMapEntry<K, V> e = tab[hash & (tab.length - 1 )];
e != null ; e = e.next) {
K eKey = e.key;
if (eKey == key || (e.hash == hash && key.equals(eKey))) {
if (accessOrder)
makeTail((LinkedEntry<K, V>) e);
return e.value;
}
}
return null ;
}
4. remove函数--同HashMap的remove函数
@Override
void postRemove(HashMapEntry<K, V> e) {
LinkedEntry<K, V> le = (LinkedEntry<K, V>) e;
le.prv.nxt = le.nxt;
le.nxt.prv = le.prv;
le.nxt = le.prv = null ;
}
二.总结
1.要了解LinedHashMap的实现,要先知道他的父类HashMap的实现,LinkedHashMap的大部分实现都在他父类中实现
2.当开启Lru算法的时候(accessOrder参数设置为true),LinkedHashMap比HashMap多的是该类自己维护双向循环链表,当增删改的时候会修改该链表,把最新的item放到末尾(Tail)
3.本质:LinkedHashMap == HashMap + LinkedList
LruCache原理分析
1.实现
实现很简单,就相当于封装了LinkedHashMap(开启Lru算法),只是每次删除的时候检查当前数据大小是否超过阈值,如果超过则调用LinkedHashMap最老的元素(循环双向链表中维护的)
2.总结
我们的缓存容量是有限的,它会面临一个问题:当有新的内容需要加入我们的缓存,但我们的缓存空闲的空间不足以放进新的内容时,如何舍弃原有的部分内容从而腾出空间用来放新的内容。解决这个问题的算法有多种,比如LRU,LFU,FIFO等。
需要注意区分的是LRU和LFU。前者是最近最少使用,即淘汰最长时间未使用的对象;后者是最近最不常使用,即淘汰一段时间内使用最少的对象。比如我们缓存对象的顺序是:A B C B D A C A ,当需要淘汰一个对象时,如果采用LRU算法,则淘汰的是B,因为它是最长时间未被使用的。如果采用LFU算法,则淘汰的是D,因为在这段时间内它只被使用了一次,是最不经常使用的。
The LRU cache is a hash table of keys and double linked nodes. The hash table makes the time of get() to be O(1). The list of double linked nodes make the nodes adding/removal operations O(1).
参考文献
LRU Cache (Java)–principle