溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

怎么在java中初始化hashmap容量

發(fā)布時間:2021-04-17 16:40:10 來源:億速云 閱讀:170 作者:Leah 欄目:編程語言

今天就跟大家聊聊有關(guān)怎么在java中初始化hashmap容量,可能很多人都不太了解,為了讓大家更加了解,小編給大家總結(jié)了以下內(nèi)容,希望大家根據(jù)這篇文章可以有所收獲。

public static void main(String[] args) {
 int aHundredMillion = 10000000;

 // 未初始化容量
 Map<Integer, Integer> map = new HashMap<>();
 long s1 = System.currentTimeMillis();
 for (int i = 0; i < aHundredMillion; i++) {
  map.put(i, i);
 }
 long s2 = System.currentTimeMillis();
 System.out.println("未初始化容量,耗時: " + (s2 - s1)); // 14322

 // 初始化容量為50000000
 Map<Integer, Integer> map1 = new HashMap<>(aHundredMillion / 2);
 long s3 = System.currentTimeMillis();
 for (int i = 0; i < aHundredMillion; i++) {
  map1.put(i, i);
 }
 long s4 = System.currentTimeMillis();
 System.out.println("初始化容量5000000,耗時: " + (s4 - s3)); // 11819

 // 初始化容量為100000000
 Map<Integer, Integer> map2 = new HashMap<>(aHundredMillion);
 long s5 = System.currentTimeMillis();
 for (int i = 0; i < aHundredMillion; i++) {
  map2.put(i, i);
 }
 long s6 = System.currentTimeMillis();
 System.out.println("初始化容量為10000000,耗時: " + (s6 - s5)); // 7978
}

從以上的代碼不難理解,我們創(chuàng)建了3個HashMap,分別使用默認(rèn)的容量(16)、使用元素個數(shù)的一半(5千萬)作為初始容量和使用元素個數(shù)(一億)作為初始容量進(jìn)行初始化,然后分別向其中put一億個KV。

從上面的打印結(jié)果中可以得到一個初步的結(jié)論:在已知HashMap中將要存放的KV個數(shù)的時候,設(shè)置一個合理的初始化容量可以有效地提高性能。下面我們來簡單分析一下原因。

我們知道,HashMap是有擴(kuò)容機(jī)制的。所謂的擴(kuò)容機(jī)制,指的是當(dāng)達(dá)到擴(kuò)容條件的時候,HashMap就會自動進(jìn)行擴(kuò)容。而HashMap的擴(kuò)容條件就是當(dāng)HashMap中的元素個數(shù)(Size)超過臨界值(Threshold)的情況下就會自動擴(kuò)容。

threshold = loadFactor * capacity

在元素個數(shù)超過臨界值的情況下,隨著元素的不斷增加,HashMap就會發(fā)生擴(kuò)容,而HashMap中的擴(kuò)容機(jī)制決定了每次擴(kuò)容都需要重建hash表,這一操作需要消耗大量資源,是非常影響性能的。因此,如果我們沒有設(shè)置初始的容量大小,HashMap就可能會不斷發(fā)生擴(kuò)容,也就使得程序的性能降低了。

另外,在上面的代碼中我們會發(fā)現(xiàn),同樣是設(shè)置了初始化容量,設(shè)置的數(shù)值不同也會影響性能,那么當(dāng)我們已知HashMap中即將存放的KV個數(shù)的時候,容量的設(shè)置就成了一個問題。

HashMap中容量的初始化

開頭提到,在默認(rèn)的情況下,當(dāng)我們設(shè)置HashMap的初始化容量時,實(shí)際上HashMap會采用第一個大于該數(shù)值的2的冪作為初始化容量。

Map<String, String> map = new HashMap<>(1);
map.put("huangq", "yanggb");

Class<?> mapType = map.getClass();
Method capacity = mapType.getDeclaredMethod("capacity");
capacity.setAccessible(true);
System.out.println("capacity : " + capacity.invoke(map)); // 2

當(dāng)初始化的容量設(shè)置成1的時候,通過反射取出來的capacity卻是2。在JDK1.8中,如果我們傳入的初始化容量為1,實(shí)際上設(shè)置的結(jié)果也是1。上面的代碼打印的結(jié)果為2的原因,是代碼中給map塞入值的操作導(dǎo)致了擴(kuò)容,容量從1擴(kuò)容到了2。事實(shí)上,在JDK1.7和JDK1.8中,HashMap初始化容量(capacity)的時機(jī)不同。在JDK1.8中,調(diào)用HashMap的構(gòu)造函數(shù)定義HashMap的時候,就會進(jìn)行容量的設(shè)定。而在JDK1.7中,要等到第一次put操作時才進(jìn)行這一操作。

因此,當(dāng)我們通過HashMap(int initialCapacity)設(shè)置初始容量的時候,HashMap并不一定會直接采用我們傳入的數(shù)值,而是經(jīng)過計(jì)算,得到一個新值,目的是提高h(yuǎn)ash的效率。比如1->1、3->4、7->8和9->16。

HashMap中初始容量的合理值

通過上面的分析我們可以知道,當(dāng)我們使用HashMap(int initialCapacity)來初始化容量的時候,JDK會默認(rèn)幫我們計(jì)算一個相對合理的值當(dāng)做初始容量。那么,是不是我們只需要把已知的HashMap中即將存放的元素個數(shù)直接傳給initialCapacity就可以了呢?

initialCapacity = (需要存儲的元素個數(shù) / 負(fù)載因子) + 1

這里的負(fù)載因子就是loaderFactor,默認(rèn)值為0.75。

initialCapacity = expectedSize / 0.75F + 1.0F

看完上述內(nèi)容,你們對怎么在java中初始化hashmap容量有進(jìn)一步的了解嗎?如果還想了解更多知識或者相關(guān)內(nèi)容,請關(guān)注億速云行業(yè)資訊頻道,感謝大家的支持。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI