前言
其實可以看到我寫了這麼久的部落格,很少去寫hashMap的東西。
為什麼?因為這個東西感覺是java面試必備的,我感覺大家都看到膩了,是以一直沒怎麼去寫hashMap相關的。
今天為什麼我突然要來寫這一篇文章,因為最近在公司看一些老項目代碼,我才發現原來其實很多人都沒用對。
本篇内容:
- 舉例說明 HashMap 使用的時候指定容量 錯誤用法;
- 源碼走讀,HashMap初始容量的 計算方式;
- 源碼走讀擴容的點;
- 正确應該怎麼去用,一定要了解再用;
- 一些雜談。
正文
不開玩笑,真的都知道指定容量,但是有些用對了,有些沒用對。
為什麼要指定容量?
這個原由,都不用說,阿裡的java開發手冊就說的很明白:
需要開發手冊的可以私信我【開發】兩個字
其實核心點,就是避免資料量慢慢增加,導緻反複觸發擴容,影響性能。
于是乎就很多錯誤的使用方式了(雖熱影響不大):
錯誤了解使用示例 ① :
分頁查詢出來的資料,需要轉換成 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);
不多舉例,其實這幾個錯誤示例,都是錯在指定容量的 值上。
預設 指定是 傳入 16, 16* 0.75=12 , 是以擴容門檻值是12 。
說到這裡,大家應該知道為什麼上面是錯誤用法了吧?
比如我們想 存 4個元素到Map, 我們為了避免後面觸發擴容影響性能(其實元素少性能沒多少影響), 就指定了 4 :
Map<Integer, String> map = new HashMap<>(4);
其實這樣 4x0.75= 3 ,那麼如果存放第四個元素的時候,就會觸發擴容
這樣就是違背了我們開始指定 的 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());
}
看下列印效果:
為什麼,當size =3 ,也就是插入三個元素的時候還沒變。
因為我們初始化容量值傳入的 4, 4* 0.75 =3. 擴容門檻值是 3!
當插入第四個元素的時候, 就超過了擴容門檻值,是以觸發了擴容,是以看的最後其實是進行了一次擴容,列印出來的capacity是 8.
那麼我們應該傳多少?
4/0.75 + 1 = 6.3333333
我們指定傳6麼?還是傳 7 ?
指定6:
指定7:
指定6,7 都沒差別好像, 值得慶祝的是,沒有再次觸發擴容。
那麼為啥沒差別呢?
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