More
More
文章目录
  1. ☞ afterNodeAccess()
  2. ☞ afterNodeInsertion()
  3. ☞ afterNodeRemoval()
  4. ☞ internalWriteEntries()

【java源码一带一路系列】之LinkedHashMap.afterNodeAccess()

本文以jdk1.8中LinkedHashMap.afterNodeAccess()方法为切入点,分析其中难理解、有价值的源码片段(类似源码查看是ctrl+鼠标左键的过程)。观光线路图:afterNodeAccess() –> afterNodeInsertion() –> removeEldestEntry() –> afterNodeRemoval() –> internalWriteEntries() …

☞ afterNodeAccess()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
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;
}
}
//////////////////////////////// 涉及变量如下:
/**
* The head (eldest) of the doubly linked list.
*/
transient LinkedHashMap.Entry<K,V> head;
/**
* The tail (youngest) of the doubly linked list.
*/
transient LinkedHashMap.Entry<K,V> tail;
/**
* The iteration ordering method for this linked hash map: <tt>true</tt>
* for access-order, <tt>false</tt> for insertion-order.
*
* @serial
*/
final boolean accessOrder;

上回在HashMap.afterNodeAccess()中说道,“是为LinkedHashMap留的后路”。如今行至于此,当观赏一方。首先需要了解的是LinkedHashMap相比HashMap多了有序性,由双向链表(before,after)实现。源码出现了一些全局变量:

accessOrder:true:按访问顺序排序(LRU),false:按插入顺序排序

head、tail:存放链表首尾

可见仅有accessOrder为true时,且访问节点不等于尾节点时,该方法才有意义。通过before、after重定向,将新访问节点链接为链表尾节点。

☞ afterNodeInsertion()

1
2
3
4
5
6
7
8
9
10
11
void afterNodeInsertion(boolean evict) { // possibly remove eldest
LinkedHashMap.Entry<K,V> first;
if (evict && (first = head) != null && removeEldestEntry(first)) {
K key = first.key;
removeNode(hash(key), key, null, false, true);
}
}
protected boolean removeEldestEntry(Map.Entry<K,V> eldest) {
return false;
}

细心的你也花现了吧。afterNodeInsertion()由于removeEldestEntry()所返回的false无执行意义。也就意味着如果想要让它有意义必须重写removeEldestEntry()。

如,使用LinkedHashMap实现一个简单的LRU(Least Recently Used)Cache。那么就应该重写removeEldestEntry(),当超出缓存容器大小时移除最老的首节点(这里不考虑并发问题,如下):

1
2
3
4
@Override
public boolean removeEldestEntry(Map.Entry<K, V> eldest){
return size() > capacity;
}

☞ afterNodeRemoval()

1
2
3
4
5
6
7
8
9
10
11
12
13
void afterNodeRemoval(Node<K,V> e) { // unlink
LinkedHashMap.Entry<K,V> p =
(LinkedHashMap.Entry<K,V>)e, b = p.before, a = p.after;
p.before = p.after = null;
if (b == null)
head = a;
else
b.after = a;
if (a == null)
tail = b;
else
a.before = b;
}

afterNodeRemoval()方法相对简单,就是在删除后处理其对应链表前后关系(刨掉一截)。

☞ internalWriteEntries()

LinkedHashMap源码阅读总体门槛相对而言比HashMap,毕竟大多数底层put,get都由HashMap实现了。internalWriteEntries()相对来说比较突兀,如果你知道它在哪里起着什么样神秘的作用请在评论里告诉在下吧。[比心❤]

1
2
3
4
5
6
void internalWriteEntries(java.io.ObjectOutputStream s) throws IOException {
for (LinkedHashMap.Entry<K,V> e = head; e != null; e = e.after) {
s.writeObject(e.key);
s.writeObject(e.value);
}
}

可通过这篇文章理解创建一个LinkedHashMap实例过程(图):

Java_LinkedHashMap工作原理 2017-05-04;

打赏
手机扫一扫,支持CHE~