天天看點

HashMap 使用的時候指定容量?你真的用明白了嗎?

前言

其實可以看到我寫了這麼久的部落格,很少去寫hashMap的東西。

為什麼?因為這個東西感覺是java面試必備的,我感覺大家都看到膩了,是以一直沒怎麼去寫hashMap相關的。

今天為什麼我突然要來寫這一篇文章,因為最近在公司看一些老項目代碼,我才發現原來其實很多人都沒用對。

本篇内容:

  • 舉例說明 HashMap 使用的時候指定容量 錯誤用法;
  • 源碼走讀,HashMap初始容量的 計算方式;
  • 源碼走讀擴容的點;
  • 正确應該怎麼去用,一定要了解再用;
  • 一些雜談。

正文

不開玩笑,真的都知道指定容量,但是有些用對了,有些沒用對。

HashMap 使用的時候指定容量?你真的用明白了嗎?

為什麼要指定容量?

這個原由,都不用說,阿裡的java開發手冊就說的很明白:

HashMap 使用的時候指定容量?你真的用明白了嗎?
需要開發手冊的可以私信我【開發】兩個字

其實核心點,就是避免資料量慢慢增加,導緻反複觸發擴容,影響性能。

于是乎就很多錯誤的使用方式了(雖熱影響不大):

錯誤了解使用示例 ① :

分頁查詢出來的資料,需要轉換成 Map, 因為分頁是固定了一頁最多15條。

是以出現了這個代碼:

Map<String, String> map = new HashMap<>(15);           

或者是

Map<String, String> map = new HashMap<>(userPageList.size());           

錯誤了解使用示例 ② :

類型type 有 4種, 要放到一個map裡面,傳回去。

是以出現了這個代碼:

Map<Integer, String> map = new HashMap<>(4);           

錯誤了解使用示例 ③:

一個參數map,裡面想放2個參數。

是以出現了這個代碼:

Map<String, String> map = new HashMap<>(2);           

不多舉例,其實這幾個錯誤示例,都是錯在指定容量的 值上。

HashMap 使用的時候指定容量?你真的用明白了嗎?

預設 指定是 傳入 16, 16* 0.75=12 , 是以擴容門檻值是12 。

說到這裡,大家應該知道為什麼上面是錯誤用法了吧?

比如我們想 存 4個元素到Map, 我們為了避免後面觸發擴容影響性能(其實元素少性能沒多少影響), 就指定了 4 :

Map<Integer, String> map = new HashMap<>(4);           

其實這樣 4x0.75= 3 ,那麼如果存放第四個元素的時候,就會觸發擴容

HashMap 使用的時候指定容量?你真的用明白了嗎?

這樣就是違背了我們開始指定 的 4 的最初用意。

實戰看看這個錯誤使用場景的情況:

同過反射,将capacity屬性的權限拿到,可以直接列印出來看下capacity的變化,就知道是否觸發了擴容:

public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {

    Map<String, String> map = new HashMap<>(4);
    Class<?> mapType = map.getClass();
    Method capacity = mapType.getDeclaredMethod("capacity");
    capacity.setAccessible(true);

    map.put("1", "第一個元素插入");
    System.out.println("capacity : " + capacity.invoke(map) + "    size : " + map.size());
    map.put("2", "第二個元素插入");
    System.out.println("capacity : " + capacity.invoke(map) + "    size : " + map.size());
    map.put("3", "第三個元素插入");
    System.out.println("capacity : " + capacity.invoke(map) + "    size : " + map.size());
    map.put("4", "第四個元素插入");
    System.out.println("capacity : " + capacity.invoke(map) + "    size : " + map.size());

}
           

看下列印效果:

HashMap 使用的時候指定容量?你真的用明白了嗎?

為什麼,當size =3 ,也就是插入三個元素的時候還沒變。

因為我們初始化容量值傳入的 4, 4* 0.75 =3. 擴容門檻值是 3!

HashMap 使用的時候指定容量?你真的用明白了嗎?

當插入第四個元素的時候, 就超過了擴容門檻值,是以觸發了擴容,是以看的最後其實是進行了一次擴容,列印出來的capacity是 8.

那麼我們應該傳多少?

4/0.75 + 1 = 6.3333333

我們指定傳6麼?還是傳 7 ?

指定6:

HashMap 使用的時候指定容量?你真的用明白了嗎?

指定7:

HashMap 使用的時候指定容量?你真的用明白了嗎?

指定6,7 都沒差別好像, 值得慶祝的是,沒有再次觸發擴容。

那麼為啥沒差別呢?

HashMap 使用的時候指定容量?你真的用明白了嗎?

HashMap會轉換成大于該capacity 的第一個2的幂作為容量 。

是以傳5,6,7,8 都是 8 ;

傳9,10,11,12,13,14,15,16 都是 16 ;

好了不多啰嗦了, 最後再補一嘴, 預設指定容量,其實就是 記憶體換性能。

是以真正去使用指定容量的時候, 需要考慮:如果我是一個定時任務,允許跑1小時。。。我需要考慮性能麼?

或者如果我服務記憶體很小,我是不是要對記憶體省吃儉用?

來源:blog.csdn.net/qq_35387940/article/details/125682065