原文地址:xeblog.cn/articles/26測試
注:本文全部代碼示例均基於
JDK8
。this
經過查看 HashMap
的源碼能夠得知其默認的初始容量爲 16
,默認的加載因子爲 0.75
。spa
/** * The default initial capacity - MUST be a power of two. * 默認的初始容量(必須是2的N次冪),默認爲2^4=16 */
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
/** * The load factor used when none specified in constructor. * 默認的加載因子爲0.75 */
static final float DEFAULT_LOAD_FACTOR = 0.75f;
複製代碼
/** * 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 */
public HashMap(int initialCapacity, float loadFactor) {
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal initial capacity: " +
initialCapacity);
if (initialCapacity > MAXIMUM_CAPACITY)
initialCapacity = MAXIMUM_CAPACITY;
if (loadFactor <= 0 || Float.isNaN(loadFactor))
throw new IllegalArgumentException("Illegal load factor: " +
loadFactor);
this.loadFactor = loadFactor;
this.threshold = tableSizeFor(initialCapacity);
}
/** * Constructs an empty <tt>HashMap</tt> with the specified initial * capacity and the default load factor (0.75). * * @param initialCapacity the initial capacity. * @throws IllegalArgumentException if the initial capacity is negative. */
public HashMap(int initialCapacity) {
this(initialCapacity, DEFAULT_LOAD_FACTOR);
}
/** * Constructs an empty <tt>HashMap</tt> with the default initial capacity * (16) and the default load factor (0.75). */
public HashMap() {
this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
}
複製代碼
通常狀況下都是經過這三種構造方法來初始化 HashMap
的。經過默認的無參構造方法初始化後 HashMap
的容量就是默認的16,加載因子也是默認的0.75;經過帶參數的構造方法能夠初始化一個自定義容量和加載因子的 HashMap
。一般狀況下,加載因子使用默認的0.75就好。code
HashMap
的容量要求必須是2的N次冪,這樣能夠提升散列的均勻性,下降 Hash
衝突的風險。可是容量能夠經過構造方法傳入的,若是我傳入一個非2次冪的數進去呢?好比3?傳進去也不會幹嗎呀,又不報錯。。。哈哈哈哈。 是的,不會報錯的,那是由於 HashMap
本身將這個數轉成了一個最接近它的2次冪的數。這個轉換的方法是 tableSizeFor(int cap)
。cdn
/** * 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;
}
複製代碼
這個方法會將傳入的數轉換成一個2次冪的數,好比傳入的是3,則返回的是4;傳入12,則返回的是16。blog
加載因子和 HashMap
的擴容機制有着很是重要的聯繫,它能夠決定在何時才進行擴容。HashMap
是經過一個閥值來肯定是否擴容,當容量超過這個閥值的時候就會進行擴容,而加載因子正是參與計算閥值的一個重要屬性,閥值的計算公式是 容量 * 加載因子
。若是經過默認構造方法建立 HashMap
,則閥值爲 16 * 0.75 = 12
,就是說當 HashMap
的容量超過12的時候就會進行擴容。內存
/** * The next size value at which to resize (capacity * load factor). * * @serial */
int threshold;
複製代碼
這是 HashMap
的 putVal(...)
方法的一個片斷,put(...)
方法其實就是調用的這個方法,size
是當前 HashMap
的元素個數,當元素個數+1後超過了閥值就會調用 resize()
方法進行擴容。ci
if (++size > threshold)
resize();
複製代碼
加載因子在通常狀況下都最好不要去更改它,默認的0.75是一個很是科學的值,它是通過大量實踐得出來的一個經驗值。當加載因子設置的比較小的時候,閥值就會相應的變小,擴容次數就會變多,這就會致使 HashMap
的容量使用不充分,還沒添加幾個值進去就開始進行了擴容,浪費內存,擴容效率還很低;當加載因子設置的又比較大的時候呢,結果又很相反,閥值變大了,擴容次數少了,容量使用率又提升了,看上去是很不錯,實際上仍是有問題,由於容量使用的越多,Hash
衝突的機率就會越大。因此,選擇一個合適的加載因子是很是重要的。get
經過默認構造方法建立一個 HashMap
,並循環添加13個值源碼
當添加第1個值後,容量爲16,加載因子爲0.75,閥值爲12
當添加完第13個值後,執行了擴容操做,容量變爲了32,加載因子不變,閥值變爲了24
建立一個初始容量爲12(非2次冪數)的 HashMap
,並添加1個值
建立一個初始容量爲2的 HashMap
,並添加2個值
當添加完第1個值後,容量爲2,加載因子爲0.75,閥值爲1
當添加完第2個值後,執行了擴容操做,容量變爲4,加載因子爲0.75,閥值爲3
建立一個初始容量爲二、加載因子爲1的 HashMap
,並添加2個值
當添加完第1個值後,容量爲2,加載因子爲1,閥值爲2
當添加完第2個值後,並無執行擴容操做,容量、加載因子、閥值均沒有變化