1. 程式人生 > >[JDK1.8]LinkedHashMap原始碼淺析

[JDK1.8]LinkedHashMap原始碼淺析

引言

上篇我們瞭解了JDK1.7中LinkedHashMap的原始碼,這篇文章試著分析一下JDK1.8中LinkedHashMap的原始碼(由於1.8HashMap做了優化,所以作為其子類的LinkedHashMap自然也做了一些修改.基於1.8的HashMap原始碼分析在之前的文章已經有了,不多闡述)

LinkedHashMap的成員變數

區別於1.7中的成員變數標誌位置只有一個header節點; JDK1.8中有一個head和一個tail節點;

JDK1.7

	/**
     * The head of the doubly linked list.
     */
    private
transient Entry<K,V> header; /** * The iteration ordering method for this linked hash map: <tt>true</tt> * for access-order, <tt>false</tt> for insertion-order. * * @serial */ private final boolean accessOrder;

JDK1.8

private static final
long serialVersionUID = 3801124242820219131L; // 用於指向雙向連結串列的頭部, 雙向連結串列頭節點(最老) transient LinkedHashMap.Entry<K,V> head; //用於指向雙向連結串列的尾部,雙向列表尾節點(最新) transient LinkedHashMap.Entry<K,V> tail; /** * 用來指定LinkedHashMap的迭代順序, * true則表示按照基於訪問的順序來排列,意思就是最近使用的entry,放在連結串列的最末尾 * false則表示按照插入順序來 */ final
boolean accessOrder;

建構函式

1.8

public LinkedHashMap(int initialCapacity, float loadFactor) {
        super(initialCapacity, loadFactor);
        accessOrder = false;
    }

    
    public LinkedHashMap(int initialCapacity) {
        super(initialCapacity);
        accessOrder = false;
    }

    /**
     * Constructs an empty insertion-ordered <tt>LinkedHashMap</tt> instance
     * with the default initial capacity (16) and load factor (0.75).
     */
    public LinkedHashMap() {
        super();
        accessOrder = false;
    }

    /**
     * Constructs an insertion-ordered <tt>LinkedHashMap</tt> instance with
     * the same mappings as the specified map.  The <tt>LinkedHashMap</tt>
     * instance is created with a default load factor (0.75) and an initial
     * capacity sufficient to hold the mappings in the specified map.
     *
     * @param  m the map whose mappings are to be placed in this map
     * @throws NullPointerException if the specified map is null
     */
    public LinkedHashMap(Map<? extends K, ? extends V> m) {
        super();
        accessOrder = false;
        putMapEntries(m, false);
    }

    /**
     * Constructs an empty <tt>LinkedHashMap</tt> instance with the
     * specified initial capacity, load factor and ordering mode.
     *
     * @param  initialCapacity the initial capacity
     * @param  loadFactor      the load factor
     * @param  accessOrder     the ordering mode - <tt>true</tt> for
     *         access-order, <tt>false</tt> for insertion-order
     * @throws IllegalArgumentException if the initial capacity is negative
     *         or the load factor is nonpositive
     */
    public LinkedHashMap(int initialCapacity,
                         float loadFactor,
                         boolean accessOrder) {
        super(initialCapacity, loadFactor);
        this.accessOrder = accessOrder;
    }

1.8的建構函式和1.7的建構函式有差異的在接收map的建構函式; 1.7

public LinkedHashMap(Map<? extends K, ? extends V> m) {
        super(m);
        accessOrder = false;
    }

super(m)呼叫hashMap的建構函式進行初始化:

public HashMap(Map<? extends K, ? extends V> m) {
        this(Math.max((int) (m.size() / DEFAULT_LOAD_FACTOR) + 1,
                      DEFAULT_INITIAL_CAPACITY), DEFAULT_LOAD_FACTOR);
        inflateTable(threshold);

        putAllForCreate(m);
    }

putAllForCreate(m)內部迴圈呼叫putForCreate(K key, V value)

private void putForCreate(K key, V value) {
        int hash = null == key ? 0 : hash(key);
        int i = indexFor(hash, table.length);

        /**
         * Look for preexisting entry for key.  This will never happen for
         * clone or deserialize.  It will only happen for construction if the
         * input Map is a sorted map whose ordering is inconsistent w/ equals.
         */
        for (Entry<K,V> e = table[i]; e != null; e = e.next) {
            Object k;
            if (e.hash == hash &&
                ((k = e.key) == key || (key != null && key.equals(k)))) {
                e.value = value;
                return;
            }
        }
		//如果沒有存在的key對應的Entry,那麼新建Entry
        createEntry(hash, key, value, i);
    }
void createEntry(int hash, K key, V value, int bucketIndex) {
        Entry<K,V> e = table[bucketIndex];
        table[bucketIndex] = new Entry<>(hash, key, value, e);
        size++;
    }

1.8

public LinkedHashMap(Map<? extends K, ? extends V> m) {
        super();
        accessOrder = false;
        putMapEntries(m, false);
    }
/**
     * Implements Map.putAll and Map constructor
     *
     * @param m the map
     * @param evict false when initially constructing this map, else
     * true (relayed to method afterNodeInsertion).
     */
    final void putMapEntries(Map<? extends K, ? extends V> m, boolean evict) {
        int s = m.size();
        if (s > 0) {
            if (table == null) { // pre-size
                float ft = ((float)s / loadFactor) + 1.0F;
                int t = ((ft < (float)MAXIMUM_CAPACITY) ?
                         (int)ft : MAXIMUM_CAPACITY);
                if (t > threshold)
                    threshold = tableSizeFor(t);
            }
            else if (s > threshold)
                resize();
            for (Map.Entry<? extends K, ? extends V> e : m.entrySet()) {
                K key = e.getKey();
                V value = e.getValue();
                //evict false when initially constructing this map, else true (relayed to method afterNodeInsertion).
                //呼叫putVal函式
                putVal(hash(key), key, value, false, evict);
            }
        }
    }

Entry

LinkedHashMap 1.8中的Entry和1.7中的區別不大,都是Map.Entry<K,V>的實現 同樣的: Entry裡面的屬性: 1、K key 2、V value 3、Entry<K, V> next 4、int hash 5、Entry<K, V> before 6、Entry<K, V> after

其中前面四個,也就是紅色部分是從HashMap.Entry中繼承過來的;後面兩個,也就是藍色部分是LinkedHashMap獨有的。不要搞錯了next和before、After,next是用於維護HashMap指定table位置上連線的Entry的順序的,before、After是用於維護Entry插入的先後順序的。

在這裡插入圖片描述

put

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