weixin_39024251 2022-06-03 22:17
浏览 13
已结题

关于Hashmap初始化大小的疑问

背景:

开发过程中,遇到需要写一个已知的hashmap,这种情况,为了防止hashmap在装载过程中,发生自动扩容,降低效率,都是要制定初始大小的;

前提:

根据本人知识储备,map的初始大小,默认为16,装载因子是0.75,即默认不指定的时候,最多可以存储12组键值对,当继续put第13组的时候,此时容量大小会扩展到32;

问题:

如果要存储3组键值对,那么根据上述情况,初始为4即可,代码实现测试,也确实如猜想

package javaBase;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;

public class Learn01 {
    public static void main(String[] args) throws InterruptedException, InvocationTargetException, NoSuchMethodException, IllegalAccessException, NoSuchFieldException {
        HashMap map = new HashMap<Integer, String>(4);
        for (int i = 0; i < 3; i++) {
            map.put(i, "1");
        }
        display(map);
    }

    public static void display(HashMap map) throws NoSuchFieldException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        Class<? extends HashMap> aClass = map.getClass();
        Field threshold = aClass.getDeclaredField("threshold");
        threshold.setAccessible(true);
        Method capacity = aClass.getDeclaredMethod("capacity");
        capacity.setAccessible(true);
        System.out.println("容量:" + capacity.invoke(map) + ";阈值:" + threshold.get(map) + ";元素数量:" + map.size());
    }

}

输出结果

容量:4;阈值:3;元素数量:3

Process finished with exit code 0

谷歌提供了guava包里面有个Maps.newHashMapWithExpectedSize(int a)函数,可以实现,输入参数想要装载的键值对个数,自动设置初始大小,但是他的逻辑是a/0.75+1,放在上面的情况,a为3时,初始的大小将为5,hashmap自动识别2的倍数,即为8


//返回一个 新的、空的hashmap实例,有足够多(expectedSize个)的节点(entries),保证添加过程不出现resize
//@return a new, empty {@code HashMap} with enough capacity to hold {@code expectedSize} entries without resizing
  public static <K, V> HashMap<K, V> newHashMapWithExpectedSize(int expectedSize) {
    return new HashMap<K, V>(capacity(expectedSize));
  }
 
  static int capacity(int expectedSize) {
    if (expectedSize < 3) {
      checkNonnegative(expectedSize, "expectedSize");
      return expectedSize + 1;
    }
    if (expectedSize < Ints.MAX_POWER_OF_TWO) { //MAX_POWER_OF_TWO = 1 << (Integer.SIZE - 2);
      // This is the calculation used in JDK8 to resize when a putAll
      // happens; it seems to be the most conservative(保守的) calculation we
      // can make.  0.75 is the default load factor.
      return (int) ((float) expectedSize / 0.75F + 1.0F);
    }
    return Integer.MAX_VALUE; // any large value //MAX_VALUE = 0x7fffffff;
}

所以,该工具类初始的大小和真正会扩容的大小差了一个键值对,为什么要这样设计,为什么不是a/0.75,而是a/0.75+1呢?
  • 写回答

0条回答 默认 最新

    报告相同问题?

    问题事件

    • 系统已结题 6月11日
    • 创建了问题 6月3日

    悬赏问题

    • ¥15 Coze智能助手搭建过程中的问题请教
    • ¥15 12864只亮屏 不显示汉字
    • ¥20 三极管1000倍放大电路
    • ¥15 vscode报错如何解决
    • ¥15 前端vue CryptoJS Aes CBC加密后端java解密
    • ¥15 python随机森林对两个excel表格读取,shap报错
    • ¥15 基于STM32心率血氧监测(OLED显示)相关代码运行成功后烧录成功OLED显示屏不显示的原因是什么
    • ¥100 X轴为分离变量(因子变量),如何控制X轴每个分类变量的长度。
    • ¥30 求给定范围的全体素数p的(p-2)/p的连乘积值
    • ¥15 VFP如何使用阿里TTS实现文字转语音?