經常被問的LinkedHashMap

LinkedHashMap簡介

LinkedHashMap是一個根據某種規則有序的hashmap。根據名字,我們也可以看出這個集合是有hash散列的功能的同時也有順序。hashmap是無法根據某種順序來訪問數據的,例如放入集合的元素先後的順序。list都有這個功能,可以根據放入集合的先後來訪問具體的數據。這裏大家也肯定是有疑問的,例如都已經使用了hash了,爲什麼還要去保證順序訪問。這個在後面的場景中解釋。

LinkedHashMap的實現

當剛遇到這個集合的時候,我也疑惑,能同時滿足條件的數據結構究竟是怎麼樣的。如果沒有思考這個問題,還請看到這裏好好想想。
---------滑稽分割--------
答案確實是沒有這樣的數據結構。他是兩種結構的組合。一種是我們熟悉的hashmap。另外一種就是鏈表。數據存入集合的時候,先根據hashmap的流程存放入數組中。然後再根據鏈表的原則,進行鏈接。
如果看源碼,也會發現其實沒有多少方法。基本都是繼承自hashmap的。

public class LinkedHashMap<K,V>
    extends HashMap<K,V>
    implements Map<K,V>

我們來看看串聯邏輯的幾個操作
節點組成

    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的節點。linkedhashmap主要增加before, after。可以組成一個雙向鏈表。
節點加入

    private void linkNodeLast(LinkedHashMap.Entry<K,V> p) {
        LinkedHashMap.Entry<K,V> last = tail;//獲取最後一個節點
        tail = p;//tail指向新加入的節點
        if (last == null)//鏈表爲空
            head = p;
        else {
            p.before = last;
            last.after = p;
        }
    }
    //新建節點的時候把節點加入了雙向鏈表
   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;
    }

刪除情況也是類似。
節點訪問

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

afterNodeAccess中主要是把訪問的節點從原來的位置摘除,加入到尾節點,成爲鏈表的最後一個元素。

使用場景

順序遍歷和快速定位
LinkedHashMap適合有加入順序和快速定位的場景。我自己開發中遇到過一個場景,就是把配置順序讀取,需要按照讀取的順序訪問,而且還需要根據值key直接獲取值。這個場景就需要使用LinkedHashMap。
緩存
LinkedHashMap另外一個強大的功能就是做緩存。不過我們要繼承一下。去重寫一個方法。

   protected boolean removeEldestEntry(Map.Entry<K,V> eldest) {//默認是沒有操作
        return false;
    }

這個方法在插入之後會被調用到。

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

LinkedHashMap支持兩種緩存策略。FIFO和LRU。大家應該也猜到控制策略的地方就是accessOrder。默認爲false。就是FIFO。設置爲true時就是LRU。因爲在訪問的時候會調整鏈表結構,調用afterNodeAccess會把訪問的節點放入隊列最後。所以每次刪除first就可以達到效果。我一般會選擇繼承LinkedHashMap。然後重寫removeEldestEntry,例如可以元素個數達到200範圍true。這裏需要根據具體場景來編寫。高質量編程shangyepingtai.xin

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