1,概述
前面講了hashMap的一些原理,LinkedHashMap繼承自hashMap,這篇文章咱們來大概看看LinkedHashMap的原理。首先說明一下,HashMap是無序的也就是不會基於插入的順序來讀取,這種場景之下就會考慮使用LinkedHashMap。html
2,LinkedHashMap與HashMap的一些關係
LinkedHashMap繼承自HashMap,也就是是HashMap的子類,在HashMap那篇文章中,有一些回調函數就是子類來實現的,因此等會咱們能夠來看看。node
HashMap數據結構存放的元素是一個內部實現的類:Node<K,V>,同理LinkedHashMap也有個相似的元素Entry,也是繼承自HashMap的Node<K,V>:數據結構
static class Entry<K,V> extends HashMap.Node<K,V> {
#多出來兩個節點:before、after 能夠組成一個雙向鏈表,保持順序。 Entry<K,V> before, after; Entry(int hash, K key, V value, Node<K,V> next) { super(hash, key, value, next); } }
3,LinkedHashMap核心成員變量
//用於指向雙向鏈表的頭部 transient LinkedHashMap.Entry<K,V> head; //用於指向雙向鏈表的尾部 transient LinkedHashMap.Entry<K,V> tail;
//用來指定LinkedHashMap的迭代順序:true表示的是按照基於訪問的順序來排序,就是把最近讀的元素,放在鏈表的尾部;false表示的是按照插入的順序來排序。
final boolean accessOrder;
4,LinkedHashMap核心方法
4.1 put()
經過查看源碼能夠發現,LinkedHashMap調用的就是HashMap的put()方法。不過LinkedHashMap複寫了其中的3個方法:newNode()、afterNodeAccess()、afterNodeInsertion()框架
newNode():
Node<K,V> newNode(int hash, K key, V value, Node<K,V> e) {
#構造了一個LinkedHashMap.Entry對象,並調用linkNodeLast方法 LinkedHashMap.Entry<K,V> p = new LinkedHashMap.Entry<K,V>(hash, key, value, e);
linkNodeLast(p); return p; }
下面看看linkNodeLast()方法:函數
private void linkNodeLast(LinkedHashMap.Entry<K,V> p) { LinkedHashMap.Entry<K,V> last = tail; tail = p; if (last == null) head = p; else { p.before = last; last.after = p; } }
下面簡單畫個圖,看看這個執行過程會怎麼樣。場景是:三個節點沒有衝突,插入的圖形是怎麼樣的(衝突就不畫圖了,其實很簡單,對着源碼來畫圖就能夠):spa
能夠看出來是一個雙向鏈表維持了一個順序,遍歷的時候只要知道首尾,就能夠維持住順序。3d
afterNodeAccess():
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; } }
這裏的accessOrder默認是false,並無執行。code
afterNodeInsertion():
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); } }
這裏的removeEldestEntry老是返回false,因此並不執行。可能又是爲了給子類複寫,來擴展的。htm
put()方法須要瞭解的點:對象
有了hashMap的鋪墊,其實這個仍是很簡單的。之因此可以按照順序,是由於插入的時候維護了一個雙向鏈表,祕密就在子類LinkedHashMap複寫了newNode方法。因此之後寫框架的話也須要擴展出一些方法,可以讓子類來回調。
4.2 get()
public V get(Object key) { Node<K,V> e; if ((e = getNode(hash(key), key)) == null) return null; if (accessOrder) afterNodeAccess(e); return e.value; }
默認狀況下get()也是和hashMap走的是同樣的。
4.3 LinkedHashMap的迭代器
可以保持順序,一是插入的時候維護的雙向鏈表,,二是在迭代器中確定使用了這種數據結構,咱們來看看它的迭代器:LinkedEntryIterator,而它的父類是LinkedHashIterator,源碼以下
abstract class LinkedHashIterator { LinkedHashMap.Entry<K,V> next; LinkedHashMap.Entry<K,V> current; int expectedModCount; LinkedHashIterator() { next = head; expectedModCount = modCount; current = null; } public final boolean hasNext() { return next != null; }
#遍歷的代碼。能夠看出來很是簡單,就是遍歷了這個雙向鏈表。 final LinkedHashMap.Entry<K,V> nextNode() {
#從首節點開始 LinkedHashMap.Entry<K,V> e = next; if (modCount != expectedModCount) throw new ConcurrentModificationException(); if (e == null) throw new NoSuchElementException(); current = e;
#依次進行遍歷。 next = e.after; return e; } public final void remove() { Node<K,V> p = current; if (p == null) throw new IllegalStateException(); if (modCount != expectedModCount) throw new ConcurrentModificationException(); current = null; K key = p.key; removeNode(hash(key), key, null, false, false); expectedModCount = modCount; } }
能夠看出來是簡單的鏈表遍歷。有興趣的朋友能夠了解下hashMap的遍歷流程。hashMap最後一個環節大概就說到這個話題了。能夠再去看看源碼,記住:源碼出真知!!
原文出處:https://www.cnblogs.com/xtz2018/p/11410102.html