LinkedHashMap源碼剖析

 

 

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

相關文章
相關標籤/搜索