LinkedHashMap 原理
基於jdk1.8
HashMap原理:http://www.cnblogs.com/zhaojj/p/7805376.html
LinkedHashMap 繼承HashMap
沒有重寫put resize等方法 因此基本數據結構是相同的數組、鏈表、紅黑樹
說說不同:
一、最基本元素存儲單元
static class Entry<K,V> extends HashMap.Node<K,V> { Entry<K,V> before, after; Entry(int hash, K key, V value, Node<K,V> next) { super(hash, key, value, next); } } 還是原HashMap的,多了兩個引用before,after,說明這是要用雙向鏈表了
二、初始化 構造方法
public LinkedHashMap() { super(); accessOrder = false; } 多了 accessOrder = false; /** * 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; accessOrder表示迭代順序,true表示訪問順序,false表示插入順序。 后面用到再具體分析
三、put方法
put方法沒有重寫,因此和HashMap是一樣的,但也有不同,不同在於LinkedHashMap實現了afterNodeAccess,afterNodeInsertion方法
看HashMap put源碼
final V putVal(int hash, K key, V value, boolean onlyIfAbsent, boolean evict) { Node<K,V>[] tab; Node<K,V> p; int n, i; if ((tab = table) == null || (n = tab.length) == 0) n = (tab = resize()).length; if ((p = tab[i = (n - 1) & hash]) == null) tab[i] = newNode(hash, key, value, null); else { Node<K,V> e; K k; if (p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k)))) e = p; else if (p instanceof TreeNode) e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value); else { for (int binCount = 0; ; ++binCount) { if ((e = p.next) == null) { p.next = newNode(hash, key, value, null); if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st treeifyBin(tab, hash); break; } if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) break; p = e; } } if (e != null) { // existing mapping for key V oldValue = e.value; if (!onlyIfAbsent || oldValue == null) e.value = value; afterNodeAccess(e); //這個方法HashMap是空實現,這里是發生hash沖突后,找到有相同key對值進行處理時調用 return oldValue; } } ++modCount; if (++size > threshold) resize(); afterNodeInsertion(evict); //這個方法HashMap也是空實現,這里是完成新數據put后調用 return null; } LinkedHashMap具體實現 1.void afterNodeAccess(Node<K,V> e) { // move node to last 注釋就說明了是把該元素移到最后 LinkedHashMap.Entry<K,V> last; if (accessOrder && (last = tail) != e) { //accessOrder用到了,默認false等於不運行,true時是按插入順序 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; } } 以上代碼看出在按插入順序時,在有相同key時,對當前節點將其移到鏈表末尾 2.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 方法在jdk1.8中固定返回false,因此不用關注此方法 說明一下removeElestEntry用於定義刪除最老元素的規則。一旦需要刪除最老節點,那么將會調用removeNode刪除節點。 舉個例子,如果一個鏈表只能維持100個元素,那么當插入了第101個元素時,以如下方式重寫removeEldestEntry的話,那么將會刪除最老的一個元素
四、get方法
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; } 唯一需要關注的就是在插入模式中,獲取值后又一次進行把當前節點移到鏈表尾部操作
五、其它
1.需要注意HashMap中TreeNode 即紅黑樹的節點類是繼承LinkedHashMap.Entry
在HashMap中沒有使用雙向鏈表,before, after沒有使用,單純的紅黑樹
在LinkedHashMap中,存取等使用的也使用紅黑樹,但維護了before, after的鏈表屬性,在存取時一樣使用紅黑樹算法,但keySet()、values()以及entrySet()等迭代時使用的是雙向鏈表來進行的
即存取是一樣 但遍歷不同,LinkedHashMap把所有插入的元素又全部重新維護了一個雙向鏈表遍歷時用的是這個鏈表,它的有序就是靠這個鏈表了玩的。簡單理解就是和hashmap都一樣,只是加了個雙向鏈表的結構用於遍歷體現有序性。
2.LinkedHashMap只有在使用三個參數的構造方法並制定accessOrder為true時,才有順序,不指定那么和HashMap基本沒什么大的區別
所謂的順序是指插入順序,而不是通常意義上的大小順序