LinkedHashMap實現原理淺析
阿新 • • 發佈:2019-02-09
LinkedHashMap是Hash表和連結串列的實現,LinkedHashMap繼承於HashMap,在使用父類方法的基礎上重新實現了用於維護連結串列的幾個方法函式,這幾個函式的作用分別是:節點訪問後、節點插入後、節點移除後做的一些事情,下面貼出原始碼。
public class LinkedHashMap<K,V> extends HashMap<K,V> implements Map<K,V>{ /** * HashMap.Node subclass for normal LinkedHashMap entries. * LinkedHashMap的條目繼承自HashMap.Node子類,這樣是為來擴充套件連結串列的指標 */ 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); } } private static final long serialVersionUID = 3801124242820219131L; /** * 雙向連結串列頭節點(最年老的),也就是最先加入連結串列中的條目 */ transient LinkedHashMap.Entry<K,V> head; /** * 雙鏈表的尾節點(最年輕的),也就是最近加入連結串列中的條目 */ transient LinkedHashMap.Entry<K,V> tail; /** *連結串列中元素的排序順序,true為按訪問先後排序,false為按插入先後排序 * @serial */ final boolean accessOrder; // 連結串列末端的連結 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; } } //刪除樹節點後需要引用替換 private void transferLinks(LinkedHashMap.Entry<K,V> src, LinkedHashMap.Entry<K,V> dst) { LinkedHashMap.Entry<K,V> b = dst.before = src.before; LinkedHashMap.Entry<K,V> a = dst.after = src.after; if (b == null) head = dst; else b.after = dst; if (a == null) tail = dst; else a.before = dst; } //使用父類HashMap的方法將此連結串列重置為初始預設狀態。 void reinitialize() { super.reinitialize(); head = tail = null; } //建立一個新的節點 Node<K,V> newNode(int hash, K key, V value, Node<K,V> e) { LinkedHashMap.Entry<K,V> p =new LinkedHashMap.Entry<K,V>(hash, key, value, e); //將節點進行關聯 linkNodeLast(p); return p; } //替換樹節點引用 Node<K,V> replacementNode(Node<K,V> p, Node<K,V> next) { LinkedHashMap.Entry<K,V> q = (LinkedHashMap.Entry<K,V>)p; LinkedHashMap.Entry<K,V> t = new LinkedHashMap.Entry<K,V>(q.hash, q.key, q.value, next); //將引用進行變更 transferLinks(q, t); return t; } //新建立一個樹節點 TreeNode<K,V> newTreeNode(int hash, K key, V value, Node<K,V> next) { TreeNode<K,V> p = new TreeNode<K,V>(hash, key, value, next); linkNodeLast(p); return p; } //替換樹節點的引用 TreeNode<K,V> replacementTreeNode(Node<K,V> p, Node<K,V> next) { LinkedHashMap.Entry<K,V> q = (LinkedHashMap.Entry<K,V>)p; TreeNode<K,V> t = new TreeNode<K,V>(q.hash, q.key, q.value, next); transferLinks(q, t); return t; } //節點刪除後, void afterNodeRemoval(Node<K,V> e) { //當前被刪除節點 LinkedHashMap.Entry<K,V> p =(LinkedHashMap.Entry<K,V>)e; //當前被刪除節點的上一節點 LinkedHashMap.Entry<K,V> b = p.before; //當前被刪除節點的下一節點 LinkedHashMap.Entry<K,V> a = p.after; //當前被刪除節點的上下節點引用清空 p.before = p.after = null; //如果上一節點不存在,則下一節點作為頭節點(被刪除節點為head時) if (b == null) head = a; //否則被刪除節點的下一節點直接作為被刪除節點的上一節點的下一節點 else b.after = a; //如果被刪除節點的下一節點不存在,則被刪除節點的上一節點就成了最後一個節點 if (a == null) tail = b; //否則被刪除節點的上一節點被作為被刪除節點的下一節點的上一節點 else a.before = b; } //節點插入後,則會根據情況判斷是否刪除超過最大容量的那些最早放入的節點 void afterNodeInsertion(boolean evict) { LinkedHashMap.Entry<K,V> first; //表頭不為null,並且removeEldestEntry(first)返回true,也就是設定為訪問排序 if (evict && (first = head) != null && removeEldestEntry(first)) { K key = first.key; removeNode(hash(key), key, null, false, true); } } //這個方法是節點元素訪問後 如get,將節點移動到最後位置 //為了便於理解連結串列排序維護,我們將按照人事組織結構說明如下實現原理 void afterNodeAccess(Node<K,V> e) { LinkedHashMap.Entry<K,V> last; //如果accessOrder為true,也就是說如果連結串列是按訪問排序並且排除掉了只有當前一個節點的情況 if (accessOrder && (last = tail) != e) { //我(當前訪問的節點) LinkedHashMap.Entry<K,V> p =(LinkedHashMap.Entry<K,V>)e; //我的直屬上司(上一個節點) LinkedHashMap.Entry<K,V> b = p.before; //我的下屬(下一個節點) LinkedHashMap.Entry<K,V> a = p.after; //將我原來那個下屬直接幹掉,人手有點多,裁員 p.after = null; //情況一:如果我沒有上司,那麼我的下屬則會成為老闆,帶頭大哥(說明訪問我時我在表頭位置) if (b == null) head = a; //否則的話,我的下屬會直屬於我的上司管,也有可能我沒有下屬,就是a = null, //要麼就是我的下屬就是我自己,那我就還是那個上司管( a = p.after = p) 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; //將職位調動次數加1 ++modCount; } } void internalWriteEntries(java.io.ObjectOutputStream s) throws IOException { for (LinkedHashMap.Entry<K,V> e = head; e != null; e = e.after) { s.writeObject(e.key); s.writeObject(e.value); } } /** * 建構函式,預設按插入方式排序 * @param initialCapacity 初始容量 * @param loadFactor 載入因子 */ public LinkedHashMap(int initialCapacity, float loadFactor) { super(initialCapacity, loadFactor); accessOrder = false; } /** * 建構函式,預設按插入方式排序 * @param initialCapacity 初始化容量 */ public LinkedHashMap(int initialCapacity) { super(initialCapacity); accessOrder = false; } /** * 建構函式,預設按插入方式排序 */ public LinkedHashMap() { super(); accessOrder = false; } /** * 建構函式,預設按插入方式排序 * @param m 實現了map介面的引數集合 */ public LinkedHashMap(Map<? extends K, ? extends V> m) { super(); accessOrder = false; putMapEntries(m, false); } /** * @param initialCapacity 初始化容量 * @param loadFactor 載入因子 * @param accessOrder 排序方式 true為訪問先後 false為插入先後 */ public LinkedHashMap(int initialCapacity, float loadFactor, boolean accessOrder) { super(initialCapacity, loadFactor); this.accessOrder = accessOrder; } /* (non-Javadoc) * @see java.util.HashMap#containsValue(java.lang.Object) */ public boolean containsValue(Object value) { for (LinkedHashMap.Entry<K,V> e = head; e != null; e = e.after) { V v = e.value; if (v == value || (value != null && value.equals(v))) return true; } return false; } public V get(Object key) { Node<K,V> e; //根據key呼叫父類的獲取節點方法,如果為null則返回null if ((e = getNode(hash(key), key)) == null) return null; //如果設定來訪問排序,那麼就需要進行訪問後的操作,將此節點移到連結串列最後 if (accessOrder) afterNodeAccess(e); //返回對應的value return e.value; } /* (non-Javadoc) * @see java.util.HashMap#getOrDefault(java.lang.Object, java.lang.Object) * 如果對應key的值為null,則返回給定預設值 */ public V getOrDefault(Object key, V defaultValue) { Node<K,V> e; if ((e = getNode(hash(key), key)) == null) return defaultValue; //按訪問排序 if (accessOrder) afterNodeAccess(e); return e.value; } /** * {@inheritDoc} */ public void clear() { super.clear(); head = tail = null; } /* * 提供給子類實現的擴充套件方法,如果返回true,則按照訪問先後排序 * */ protected boolean removeEldestEntry(Map.Entry<K,V> eldest) { return false; }