Java集合類——LinkedHashMap

一、概述

在總結了HashMap以後,現在來看看LikedHashMap的工作原理以及實現。首先還是先整一段LinkedHashMap程序:

LinkedHashMap<String,Integer> lmap = new LinkedHashMap<String,Integer>();
lmap.put("語文", 1);
lmap.put("數學", 2);
lmap.put("英語", 3);
lmap.put("歷史", 4);
lmap.put("政治", 5);
lmap.put("地理", 6);
lmap.put("生物", 7);
lmap.put("化學", 8);
for(Entry<String, Integer> entry : lmap.entrySet()) {
    System.out.println(entry.getKey() + ": " + entry.getValue());
}

運行結果是:

語文: 1 數學: 2 英語: 3 歷史: 4 政治: 5 地理: 6 生物: 7 化學: 8

這個結果和上一次的HashMap的運行結果不一樣,LinkedHashMap的迭代輸出結果保持了插入的順序。是什麼樣的結構是得LinkedHashMap具有如此特性呢?讓我們來看看LinkedHashMap的內部結構,有一個認識:

LinkedHashMap是Hash表和鏈表的實現,並且依靠雙向鏈表保證了迭代順序是插入的順序。

二、三個重點實現的函數

在HashMap中提到下面的定義:

void afterNodeAccess(Node<K,V> p) { }
void afterNodeInsertion(boolean evict) { }
void afterNodeRemoval(Node<K,V> p) { }

LinkedHashMap繼承於HashMap,因此也重新實現了這3個函數,顧名思義這三個函數的作用分別是:節點訪問後、節點插入後、節點移除後做一些事情。

afterNodeAccess函數

void afterNodeAccess(Node<K,V> e) { // move node to last
    LinkedHashMap.Entry<K,V> last;
    // 如果定義了accessOrder,那麼就保證最近訪問節點放到最後
    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;
    }
}

就是說在進行put之後就算是對節點的訪問了,那麼這個時候就會更新鏈表,把最近訪問的放到最後,保證鏈表插入的有序性。

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);
    }
}

afterNodeRemoval函數

void afterNodeRemoval(Node<K,V> e) { // unlink
    // 從鏈表中移除節點
    LinkedHashMap.Entry<K,V> p =
        (LinkedHashMap.Entry<K,V>)e, b = p.before, a = p.after;
    p.before = p.after = null;
    if (b == null)
        head = a;
    else
        b.after = a;
    if (a == null)
        tail = b;
    else
        a.before = b;
}

這個函數是在移除節點後調用的,就是將節點從雙向鏈表中刪除。

從上面的3個函數看出,基本上是爲了保證雙向鏈表中的節點次序或者雙向鏈表容量所作的一些額外的事情,目的就是保持雙向鏈表中節點的順序要從eidest到youngest。

三、put和get函數

put函數在LinkedHashMap中沒有重新實現,只是實現了afterNodeAccess和afterNodeInsertion兩個回調函數。get函數則是重新實現並加入afterNodeAccess來保證訪問順序,下面是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;
}

在accessOrder模式下,只要執行了get或者put操作的時候,就會產生structural modification,LinkedHashMap的其他操作基本上是爲了維護具有訪問順序的雙向鏈表。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章