Java 容器 LinkedHashMap源码分析2

一、类签名

LinkedHashMap继承自HashMap,可知存入的节点key永远是唯一的。可以通过Android的LruCache了解LinkedHashMap用法。

1 public class LinkedHashMap
2     extends HashMap
3     implements Map

 

Java 容器 LinkedHashMap源码分析2_第1张图片

二、节点

EntryHashMap.Node的子类,增加beforeafter引用实现双向链表

1 static class Entry extends HashMap.Node {
2     // 前节点、后节点
3     Entry before, after;
4 
5     Entry(int hash, K key, V value, Node next) {
6         // 调用HashMap构造方法
7         super(hash, key, value, next);
8     }
9 }

 

Java 容器 LinkedHashMap源码分析2_第2张图片

三、数据成员

双向链表头,指向最早(最老)访问节点元素

transient LinkedHashMap.Entry<K,V> head; 

双向链表尾,指向最近(最晚)访问节点元素

transient LinkedHashMap.Entry<K,V> tail; 

是否保持访问顺序,为true则每次被访问的节点都会放到链表尾部

final boolean accessOrder;

依次插入Entry_0Entry_5,当accessOrder为true并访问Entry_4时,Entry_4会被移到链尾

Java 容器 LinkedHashMap源码分析2_第3张图片

四、构造方法

 1 public LinkedHashMap(int initialCapacity, float loadFactor) {
 2     super(initialCapacity, loadFactor);
 3     accessOrder = false;
 4 }
 5 
 6 public LinkedHashMap(int initialCapacity) {
 7     super(initialCapacity);
 8     accessOrder = false;
 9 }
10 
11 public LinkedHashMap() {
12     super();
13     accessOrder = false;
14 }
15 
16 public LinkedHashMap(Mapextends K, ? extends V> m) {
17     super();
18     accessOrder = false;
19     putMapEntries(m, false);
20 }
21 
22 // 维持存取顺序仅能通过此构造方法
23 public LinkedHashMap(int initialCapacity,
24                      float loadFactor,
25                      boolean accessOrder) {
26     super(initialCapacity, loadFactor);
27     this.accessOrder = accessOrder;
28 }

 

五、成员方法

 1 // 把节点插入到链表尾部
 2 private void linkNodeLast(LinkedHashMap.Entry p) {
 3     LinkedHashMap.Entry last = tail;
 4     tail = p;
 5     // 如果尾节点为空表明链表没有元素,则p就是头结点
 6     if (last == null)
 7         head = p;
 8     else {
 9         // 处理双向链表节点
10         p.before = last;
11         last.after = p;
12     }
13 }
14 
15 // apply src's links to dst
16 private void transferLinks(LinkedHashMap.Entry src,
17                            LinkedHashMap.Entry dst) {
18     LinkedHashMap.Entry b = dst.before = src.before;
19     LinkedHashMap.Entry a = dst.after = src.after;
20     if (b == null)
21         head = dst;
22     else
23         b.after = dst;
24     if (a == null)
25         tail = dst;
26     else
27         a.before = dst;
28 }
29 
30 // 重写HashMap钩子方法
31 void reinitialize() {
32     super.reinitialize();
33     head = tail = null;
34 }
35 
36 // 创建新链表节点
37 Node newNode(int hash, K key, V value, Node e) {
38     LinkedHashMap.Entry p =
39         new LinkedHashMap.Entry<>(hash, key, value, e);
40     linkNodeLast(p);
41     return p;
42 }
43 
44 // 替换链表节点
45 Node replacementNode(Node p, Node next) {
46     LinkedHashMap.Entry q = (LinkedHashMap.Entry)p;
47     LinkedHashMap.Entry t =
48         new LinkedHashMap.Entry<>(q.hash, q.key, q.value, next);
49     transferLinks(q, t);
50     return t;
51 }
52 
53 // 创建新红黑树节点
54 TreeNode newTreeNode(int hash, K key, V value, Node next) {
55     TreeNode p = new TreeNode<>(hash, key, value, next);
56     linkNodeLast(p);
57     return p;
58 }
59 
60 // 替换红黑树节点
61 TreeNode replacementTreeNode(Node p, Node next) {
62     LinkedHashMap.Entry q = (LinkedHashMap.Entry)p;
63     TreeNode t = new TreeNode<>(q.hash, q.key, q.value, next);
64     transferLinks(q, t);
65     return t;
66 }

 

六、顺序操作

 1 // 把节点从链表解除链接
 2 void afterNodeRemoval(Node e) {
 3     // p:即是节点e
 4     // b:e的前一个节点
 5     // a:e的后一个节点
 6     LinkedHashMap.Entry p =
 7         (LinkedHashMap.Entry)e, b = p.before, a = p.after;
 8 
 9     // 置空节点p的前后引用
10     p.before = p.after = null;
11     
12     if (b == null) {
13         // 可知节点e是链表头结点,则e的下一个节点a作为链表的头结点
14         head = a;
15     } else {
16         // 可知节点e本是中间结点,把e下一个节点a作为e上一个节点的后续节点
17         b.after = a;
18     }
19 
20     if (a == null) {
21         // 可知节点e本是链表尾节点,则e的上一个节点b作为链表的尾节点
22         tail = b;
23     } else {
24         // 可知节点e本身是中间节点,把e上一个节点b作为e下一个节点的前置节点
25         a.before = b;
26     }
27 }
28 
29 // 父类HashMap调用putVal()中会调用此方法,移除最少使用的节点
30 void afterNodeInsertion(boolean evict) {
31     // first是最少使用的节点
32     LinkedHashMap.Entry first;
33     if (evict && (first = head) != null && removeEldestEntry(first)) {
34         // 获取first的key
35         K key = first.key;
36         // 通过key找到对应Node并移除
37         removeNode(hash(key), key, null, false, true);
38     }
39 }
40 
41 // 把节点移动到链表尾
42 void afterNodeAccess(Node e) {
43     LinkedHashMap.Entry last;
44     // 仅当accessOrder为true且被访问元素不是尾节点
45     if (accessOrder && (last = tail) != e) {
46         // p:即是节点e
47         // b:e的前一个节点
48         // a:e的后一个节点
49         LinkedHashMap.Entry p =
50             (LinkedHashMap.Entry)e, b = p.before, a = p.after;
51 
52         // 置空节点p的后引用
53         p.after = null;
54 
55         if (b == null) {
56             // 可知节点e本是头结点,则e的下一个节点a作为链表的头结点
57             head = a;
58         } else {
59             // 可知节点e本是中间结点,把e下一个节点a作为e上一个节点的后续节点
60             b.after = a;
61         }
62 
63         if (a != null) {
64             // 可知节点e本身是中间节点,把e上一个节点b作为e下一个节点的前置节点
65             a.before = b;
66         } else {
67             // 可知节点e本是尾节点,则e的上一个节点b作为链表的尾节点
68             last = b;
69         }
70         
71         // p之前没有节点,表明p就是头结点
72         if (last == null) {
73             head = p;
74         } else {
75             // p作为新的尾节点,链接到上一个尾节点之后
76             p.before = last;
77             last.after = p;
78         }
79 
80         tail = p; // tail引用指向p
81         ++modCount; // 修改次数递增
82     }
83 }

 

七、获取

 1 // 检查LinkedHashMap是否包含指定value
 2 public boolean containsValue(Object value) {
 3     // 从链表头结点开始遍历,逐个查找Entry.value是否等于value
 4     for (LinkedHashMap.Entry e = head; e != null; e = e.after) {
 5         V v = e.value;
 6         if (v == value || (value != null && value.equals(v)))
 7             return true; // 包含对应value,返回true
 8     }
 9     return false; // 不包含对应value,返回false
10 }
11 
12 // 通过Key获取对应Entry的value
13 public V get(Object key) {
14     Node e;
15     if ((e = getNode(hash(key), key)) == null) {
16         // 通过key获取Node为空,返回null作为结果
17         return null;
18     }
19 
20     if (accessOrder) {
21         // 通过key获取Node不为空,执行afterNodeAccess(e)调整顺序
22         afterNodeAccess(e);
23     }
24 
25     return e.value; // 最后把获取的Entry.value返回
26 }
27 
28 // 通过Key获取对应Entry的value
29 public V getOrDefault(Object key, V defaultValue) {
30    Node e;
31     if ((e = getNode(hash(key), key)) == null) {
32        // 通过key获取Node为空,返回defaultValue作为结果
33        return defaultValue;
34     }
35     
36     if (accessOrder) {
37         // 通过key获取Node不为空,执行afterNodeAccess(e)调整顺序
38         afterNodeAccess(e);
39     }
40 
41    return e.value; // 最后把获取的Entry.value返回
42 }

 

八、移除

 1 // 清除所有引用
 2 public void clear() {
 3     super.clear(); // 把HashMap所有Entry都清空
 4     head = tail = null;  // 置空head和tail引用
 5 }
 6 
 7 // 方法主要用于被子类重写,决定最少使用的节点能否被移除
 8 protected boolean removeEldestEntry(Map.Entry eldest) {
 9     return false;
10 } 

 

 


你可能感兴趣的:(Java 容器 LinkedHashMap源码分析2)