淺談HashMap實現原理

咱們都知道數組鏈表的優劣,那HashMap有效地整合了數組和鏈表,造成了新的一種數據裝載模型。數組

 

利用數組+鏈表的優點this

一、減小數組沒必要要空間的開闢(假如單純利用數組實現裝載,咱們總要考慮預先分配一部份內存,總不能須要的時候纔開闢吧?在設計理念上也不符),利用鏈表可以更好地實現動態分配內存(經過引用關係指定);spa

二、經過散列均勻地將下標一致的對象掛在相應的鏈表下,這樣的好處是經過數組和鏈表的整合減小了查詢的複雜度(遍歷最大次數爲 數組長度+某鏈表下長度)設計

三、總之一句話「悟天克斯的合體」code

 

HashMap最基本的結構是由Entry 一個內部靜態類定義的對象

 static class Entry<K,V> implements Map.Entry<K,V> {
   final K key;//key
        V value;//value
        Entry<K,V> next;//鏈表結構,指定的下一個Entry
        int hash;//經過散列獲取的惟一的hash值,在put環節將做爲一部分條件來驗證Entry是否重複

        /**
         * Creates new entry.
         */
        Entry(int h, K k, V v, Entry<K,V> n) {
            value = v;
            next = n;
            key = k;
            hash = h;
        }

        public final K getKey() {
            return key;
        }

        public final V getValue() {
            return value;
        }

        public final V setValue(V newValue) {
            V oldValue = value;
            value = newValue;
            return oldValue;
        }

        public final boolean equals(Object o) {
            if (!(o instanceof Map.Entry))
                return false;
            Map.Entry e = (Map.Entry)o;
            Object k1 = getKey();
            Object k2 = e.getKey();
            if (k1 == k2 || (k1 != null && k1.equals(k2))) {
                Object v1 = getValue();
                Object v2 = e.getValue();
                if (v1 == v2 || (v1 != null && v1.equals(v2)))
                    return true;
            }
            return false;
        }

        public final int hashCode() {
            return Objects.hashCode(getKey()) ^ Objects.hashCode(getValue());
        }

        public final String toString() {
            return getKey() + "=" + getValue();
        }

        /**
         * This method is invoked whenever the value in an entry is
         * overwritten by an invocation of put(k,v) for a key k that's already
         * in the HashMap.
         */
        void recordAccess(HashMap<K,V> m) {
        }

        /**
         * This method is invoked whenever the entry is
         * removed from the table.
         */
        void recordRemoval(HashMap<K,V> m) {
        }
    }

 

如下是咱們經常使用的put方法blog

public V put(K key, V value) {
        //是否須要初始化,區別在於put是不是第一次操做,若是是第一次操做將會初始化一個數組table出來
        if (table == EMPTY_TABLE) {
            inflateTable(threshold);
        }
        //HashMap是能夠put key爲null的鍵值對的,此Entry將會放在table[0]所在的鏈表下,具體在鏈表的什麼位置只能看他是何時put進去的了,畢竟鏈表結構是先來得先排前面
        if (key == null)
            return putForNullKey(value);
        //經過散列獲取hash
        int hash = hash(key);
        //經過hash和table長度肯定所屬鏈表 下標(分組)
        int i = indexFor(hash, table.length);
        for (Entry<K,V> e = table[i]; e != null; e = e.next) {
            Object k;
            //關於HashMap put如何肯定put的key重複 在此處作了邏輯判斷
            if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
                V oldValue = e.value;
                e.value = value;
                e.recordAccess(this);
                return oldValue;
            }
        }

        modCount++;
        //後續會有擴容操做
        addEntry(hash, key, value, i);
        return null;
    }        

 

有同窗不解了,如何經過hash和長度肯定所屬的鏈表(所屬的組,將鏈表看做一個組)內存

 //經過hash和table長度肯定所屬鏈表 下標(分組) int i = indexFor(hash, table.length);
 /**
     * Returns index for hash code h.
     */
    static int indexFor(int h, int length) {
        // assert Integer.bitCount(length) == 1 : "length must be a non-zero power of 2";
        //經過與運算 返回了一個 0至length-1之間的int數據
        return h & (length-1);
    }
 

接下來就是找好隊長去排隊,若是沒有隊長那麼我來擔任隊長rem

Entry<K,V> e = table[i]; e != null; e = e.next

 

待更!get

相關文章
相關標籤/搜索