Java 容器 LinkedHashMap源码分析2
一、类签名
LinkedHashMap<K,V>
继承自HashMap<K,V>,可知存入的节点key永远是唯一的。可以通过Android的LruCache了解LinkedHashMap
用法。
1 public class LinkedHashMap<K,V> 2 extends HashMap<K,V> 3 implements Map<K,V>
二、节点
Entry<K,V>
是HashMap.Node<K,V>
的子类,增加before
、after
引用实现双向链表
1 static class Entry<K,V> extends HashMap.Node<K,V> { 2 // 前节点、后节点 3 Entry<K,V> before, after; 4 5 Entry(int hash, K key, V value, Node<K,V> next) { 6 // 调用HashMap构造方法 7 super(hash, key, value, next); 8 } 9 }
三、数据成员
双向链表头,指向最早(最老)访问节点元素
transient LinkedHashMap.Entry<K,V> head;
双向链表尾,指向最近(最晚)访问节点元素
transient LinkedHashMap.Entry<K,V> tail;
是否保持访问顺序,为true则每次被访问的节点都会放到链表尾部
final boolean accessOrder;
依次插入Entry_0
到Entry_5
,当accessOrder
为true并访问Entry_4
时,Entry_4
会被移到链尾
四、构造方法
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(Map<? extends 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<K,V> p) { 3 LinkedHashMap.Entry<K,V> 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<K,V> src, 17 LinkedHashMap.Entry<K,V> dst) { 18 LinkedHashMap.Entry<K,V> b = dst.before = src.before; 19 LinkedHashMap.Entry<K,V> 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<K,V> newNode(int hash, K key, V value, Node<K,V> e) { 38 LinkedHashMap.Entry<K,V> p = 39 new LinkedHashMap.Entry<>(hash, key, value, e); 40 linkNodeLast(p); 41 return p; 42 } 43 44 // 替换链表节点 45 Node<K,V> replacementNode(Node<K,V> p, Node<K,V> next) { 46 LinkedHashMap.Entry<K,V> q = (LinkedHashMap.Entry<K,V>)p; 47 LinkedHashMap.Entry<K,V> t = 48 new LinkedHashMap.Entry<>(q.hash, q.key, q.value, next); 49 transferLinks(q, t); 50 return t; 51 } 52 53 // 创建新红黑树节点 54 TreeNode<K,V> newTreeNode(int hash, K key, V value, Node<K,V> next) { 55 TreeNode<K,V> p = new TreeNode<>(hash, key, value, next); 56 linkNodeLast(p); 57 return p; 58 } 59 60 // 替换红黑树节点 61 TreeNode<K,V> replacementTreeNode(Node<K,V> p, Node<K,V> next) { 62 LinkedHashMap.Entry<K,V> q = (LinkedHashMap.Entry<K,V>)p; 63 TreeNode<K,V> t = new TreeNode<>(q.hash, q.key, q.value, next); 64 transferLinks(q, t); 65 return t; 66 }
六、顺序操作
1 // 把节点从链表解除链接 2 void afterNodeRemoval(Node<K,V> e) { 3 // p:即是节点e 4 // b:e的前一个节点 5 // a:e的后一个节点 6 LinkedHashMap.Entry<K,V> p = 7 (LinkedHashMap.Entry<K,V>)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<K,V> 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<K,V> e) { 43 LinkedHashMap.Entry<K,V> last; 44 // 仅当accessOrder为true且被访问元素不是尾节点 45 if (accessOrder && (last = tail) != e) { 46 // p:即是节点e 47 // b:e的前一个节点 48 // a:e的后一个节点 49 LinkedHashMap.Entry<K,V> p = 50 (LinkedHashMap.Entry<K,V>)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<K,V> 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<K,V> 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<K,V> 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<K,V> eldest) { 9 return false; 10 }