HashMap源碼解析(JDK1.8)

HashMap

首先我們看一下類定義的頭部,

繼承了一個抽象的AbstractMap類,實現了3個基本的接口,其中一個是集合接口下的Map接口。

基本的類成員變量如下所示:

static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16

static final int MAXIMUM_CAPACITY = 1 << 30;

static final float DEFAULT_LOAD_FACTOR = 0.75f;

static final int TREEIFY_THRESHOLD = 8;

static final int UNTREEIFY_THRESHOLD = 6;

static final int MIN_TREEIFY_CAPACITY = 64;

以及一個內部靜態類

static class Node<K,V> implements Map.Entry<K,V> {...}

 

先來說一下上面的那些靜態常量的用途,

DEFAULT_INITIAL_CAPACITY 用於表示默認初始化時的map容量大小,這邊是1左移4位,也就是1*2*2*2*2,2的4次,爲16。

MAXIMUM_CAPACITY 表示限定的最大的容量,此處是2的30次

DEFAULT_LOAD_FACTOR 表示默認的填充比(擴展容量的係數),當前爲0.75f

TREEIFY_THRESHOLD 表示一個桶的樹化閾值,當前爲8

UNTREEIFY_THRESHOLD 表示一個樹的鏈表還原閾值(必須比TREEIFY_THRESHOLD 小),當前爲6

MIN_TREEIFY_CAPACITY 表示哈希表的最小樹形化容量

 

然後接下來就是一個基本的鏈表,靜態內部類

static class Node<K,V> implements Map.Entry<K,V> {}

具體的實現如下所示,就是一個基本的鏈表實現,然後其中多了一個【final int hash】;這個的用途會在後面講到。

static class Node<K,V> implements Map.Entry<K,V> {

final int hash;

final K key;

V value;

Node<K,V> next;



Node(int hash, K key, V value, Node<K,V> next) {

this.hash = hash;

this.key = key;

this.value = value;

this.next = next;

}



public final K getKey() { return key; }

public final V getValue() { return value; }

public final String toString() { return key + "=" + value; }



public final int hashCode() {

// 計算hash值

return Objects.hashCode(key) ^ Objects.hashCode(value);

}



public final V setValue(V newValue) {

V oldValue = value;

value = newValue;

return oldValue;

}



public final boolean equals(Object o) {

if (o == this)

return true;

if (o instanceof Map.Entry) {

Map.Entry<?,?> e = (Map.Entry<?,?>)o;

if (Objects.equals(key, e.getKey()) &&

Objects.equals(value, e.getValue()))

return true;

}

return false;

}

}





static final int hash(Object key) {

int h;

return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);

} //獲取hash值



一個比較函數
/**

* Returns k.compareTo(x) if x matches kc (k's screened comparable

* class), else 0.

* 如果x的類型是kc,返回k.compareTo(x)的比較結果 * 如果x爲空,或者類型不是kc,返回0

*/

@SuppressWarnings({"rawtypes","unchecked"}) // for cast to Comparable

static int compareComparables(Class<?> kc, Object k, Object x) {

return (x == null || x.getClass() != kc ? 0 :

((Comparable)k).compareTo(x));

}

 

tableSizeFor方法是找到下一個最小的比入參大的2的高次冪。| 這個是按位或,>>>這個是無符號右移(大於輸入參數且最近的2的整數次冪的數)。參考 https://www.cnblogs.com/loading4/p/6239441.html 以及https://zhidao.baidu.com/question/291266003.html

/**

* Returns a power of two size for the given target capacity.

*/
static final int tableSizeFor(int cap) {

int n = cap - 1;

n |= n >>> 1;

n |= n >>> 2;

n |= n >>> 4;

n |= n >>> 8;

n |= n >>> 16;

return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;

}



 

/**

* The table, initialized on first use, and resized as

* necessary. When allocated, length is always a power of two.

* (We also tolerate length zero in some operations to allow

* bootstrapping mechanics that are currently not needed.)

*/

位桶數組,存儲位桶,容量總爲2的冪次

transient Node<K,V>[] table;

 

/**

* Holds cached entrySet(). Note that AbstractMap fields are used

* for keySet() and values().

*/存儲鍵值對的set

transient Set<Map.Entry<K,V>> entrySet;
/**

* The next size value at which to resize (capacity * load factor).

*

* @serial

*/下一次容量重設(擴容)的臨界值,一般爲2的冪次-1

int threshold;
/**

* The load factor for the hash table.

*

* @serial

*/填充比

final float loadFactor;

 

接下來是一些公共的方法

 

首先是4個構造函數

/**

* Constructs an empty <tt>HashMap</tt> with the specified initial

* capacity and load factor.

*

* @param initialCapacity the initial capacity

* @param loadFactor the load factor

* @throws IllegalArgumentException if the initial capacity is negative

* or the load factor is nonpositive

*/

HashMap的帶 填充比(loadFactor)的初始化方法,代碼基本簡單直觀

public HashMap(int initialCapacity, float loadFactor) {
    if (initialCapacity < 0)//初始容量不能小於0
        throw new IllegalArgumentException("Illegal initial capacity: " +
                                           initialCapacity);
//初始容量不能大於最大容量上限。
    if (initialCapacity > MAXIMUM_CAPACITY)
        initialCapacity = MAXIMUM_CAPACITY;
//填充比不能小於0,不能爲非數字。
    if (loadFactor <= 0 || Float.isNaN(loadFactor))
        throw new IllegalArgumentException("Illegal load factor: " +
                                           loadFactor);
    this.loadFactor = loadFactor;
    this.threshold = tableSizeFor(initialCapacity);
}



/**

採用默認填充比的構造函數

*/

public HashMap(int initialCapacity) {
    this(initialCapacity, DEFAULT_LOAD_FACTOR);
}



/**

容量爲16以及採取默認填充比的構造函數

* (16) and the default load factor (0.75).

*/

public HashMap() {
    this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
}



/**

用m的元素去構造hashmap,採用的填充比爲默認的0.75f


*/

public HashMap(Map<? extends K, ? extends V> m) {
    this.loadFactor = DEFAULT_LOAD_FACTOR;
    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).

將用m的元素全部放入hashmap中,初次構建這個hashmap的時候evict爲false,否則爲true

*/
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();
            putVal(hash(key), key, value, false, evict);
        }
    }
}

未完待續

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