前置知識
先來看看HashMap中的成員屬性
解釋:
-
size
當(dāng)前的容器中Entry的數(shù)量,也就是當(dāng)前K-V的數(shù)量 -
loadFactory
裝載因子,用來衡量HashMap滿的程度,loadFactory的默認值是0.75 -
threshold
臨界值,當(dāng)實際KV數(shù)量超過threshold時,就會觸發(fā)擴容機制。threshold = capatity * loadFactory
容量capatity
除了以上這些成員屬性外,還有一個緊密關(guān)聯(lián)的概念:capatity容量。
capatity與size不是同一個概念,size來表示當(dāng)前的HashMap已經(jīng)裝了多少,capatity是容量,HashMap最多能裝多少
利用反射獲取HashMap的容量capatity的大小
看好這個方法,接下來要用
public static void printHashMapCapacity(HashMap map) throws Exception {
Class<? extends HashMap> mapClass = map.getClass();
Method capacity = mapClass.getDeclaredMethod("capacity");
capacity.setAccessible(true);
Integer invoke = (Integer) capacity.invoke(map);
System.out.println(invoke);
}
如果利用HashMap的無參構(gòu)造,默認情況下的容量是16,當(dāng)達到擴容機制時,就會擴容到32,以此類推
如果在new時,指定容量大小,則HashMap會選擇第一個大于等于此數(shù)字的2的冪次作為初始容量
HashMap<Object, Object> hashMap = new HashMap<>();
printHashMapCapacity(hashMap);
// 默認的容量16
HashMap<Object, Object> map2 = new HashMap<>(1);
printHashMapCapacity(map2);
// 1 , 因為2的0次方是1
HashMap<Object, Object> map3 = new HashMap<>(7);
printHashMapCapacity(map3);
// 8 , 第一個大于7的2的次冪是8
阿里Java開發(fā)手冊中規(guī)定,在初始化HashMap時,應(yīng)該盡量指定其初始大小
loadFactory和threshold
這兩個屬性用來指定HashMap的擴容機制
threshold = loadFactory * capacity
當(dāng)HashMap中的size超過threshold時,就會觸發(fā)擴容,每次擴容后的容量是原始容量的2倍
從默認的初始容量16擴容到32、64、128…
loadFactory是裝載因子,用來衡量HashMap滿的程度,默認值是0.75f。
設(shè)置成0.75的好處是:capacity是2的次冪,兩個數(shù)的乘積還是整數(shù),避免浮點數(shù)造成影響。
我們初始化HashMap時,除了可以指定初始化容量大小外,還可以指定裝載因子的大小的,但是修改裝載因子的值是不推薦的。
為什么要設(shè)置初始化容量
當(dāng)HashMap 發(fā)生擴容時,會新建一個指定容量的桶,然后將原來的Entry拷貝到新的桶中。
此過程會有較大的資源消耗。
為了避免在向HashMap中put過多元素時頻繁發(fā)生擴容,需要指定一個合適大小的初始化容量。
初始化容量設(shè)置多少合適
我們的目的是避免頻繁發(fā)生擴容,所以一次性指定初始化容量合適大小。
并不是我要塞入多少個元素,就初始化為多少,來看一個例子:
當(dāng)我要向HashMap中put 7個元素,我將HashMap初始化為7
HashMap map = new HashMap(7);
因為傳入的是一個7,HashMap會選擇一個大于等于該值的2的次冪作為容量capacity
根據(jù)loadFactory=0.75,計算出threshold = 8 * 0.75 = 6
當(dāng)我們put第7個元素時,就會觸發(fā)擴容,擴容到16。
但是我們就想put 7個元素,在第7個時觸發(fā)擴容,導(dǎo)致了性能和空間的浪費。
所以,我們在初始化時,選擇多少的初始化容量,值得考慮。
借鑒HashMap 中putAll()方法,得出以下公式:
initCapatity = expectedSize / 0.75F + 1.0F
這個計算方法雖然浪費了一些空間,但是在性能上卻提升了。
當(dāng)我們要put 7個元素, initCapacity = 7 / 0.75 + 1 = 10 ,經(jīng)過HashMap的計算,第一個大于10的2的次冪的數(shù)是16,所以HashMap的容量是16。
16 * 0.75 = 12 ,當(dāng)我要put第7個元素時,并不會發(fā)生擴容,性能得到提升。
而且與第一次我們設(shè)置初始化容量為7相比,最終所占用的空間是一樣的。
為什么選擇2的次冪作為容量
因為,在put時,HashMap會根據(jù)key的hash來計算對應(yīng)Entry所在桶的位置。
通常都是利用求余來實現(xiàn)的
index = hash % capacity
但是直接利用%來計算是非常慢的,效率非常第低。
我們知道,在計算機中,最快的運算就是位運算了。
存在這樣一個規(guī)律:當(dāng)capacity是2的次冪時,滿足以下恒等式文章來源:http://www.zghlxwxcb.cn/news/detail-426007.html
hash % capacity == hash & (capacity - 1)
在我們創(chuàng)建HashMap時,就做了第一步處理,選擇大于等于給定數(shù)字的第一個2的次冪作為容量,就保證了。文章來源地址http://www.zghlxwxcb.cn/news/detail-426007.html
到了這里,關(guān)于HashMap的擴容機制、初始化容量大小的選擇、容量為什么是2的次冪的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!