当前位置: 首页 > news >正文

HashMap的扩容机制、初始化容量大小的选择、容量为什么是2的次幂

前置知识

先来看看HashMap中的成员属性

解释:

  • size当前的容器中Entry的数量,也就是当前K-V的数量
  • loadFactory装载因子,用来衡量HashMap满的程度,loadFactory的默认值是0.75
  • threshold临界值,当实际KV数量超过threshold时,就会触发扩容机制
    threshold = capatity * loadFactory

容量capatity

除了以上这些成员属性外,还有一个紧密关联的概念:capatity容量。
capatity与size不是同一个概念,size来表示当前的HashMap已经装了多少,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的无参构造,默认情况下的容量是16,当达到扩容机制时,就会扩容到32,以此类推
如果在new时,指定容量大小,则HashMap会选择第一个大于等于此数字的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开发手册中规定,在初始化HashMap时,应该尽量指定其初始大小

loadFactory和threshold

这两个属性用来指定HashMap的扩容机制
threshold = loadFactory * capacity
当HashMap中的size超过threshold时,就会触发扩容,每次扩容后的容量是原始容量的2倍
从默认的初始容量16扩容到32、64、128…
loadFactory是装载因子,用来衡量HashMap满的程度,默认值是0.75f。
设置成0.75的好处是:capacity是2的次幂,两个数的乘积还是整数,避免浮点数造成影响。
我们初始化HashMap时,除了可以指定初始化容量大小外,还可以指定装载因子的大小的,但是修改装载因子的值是不推荐的。

为什么要设置初始化容量

当HashMap 发生扩容时,会新建一个指定容量的桶,然后将原来的Entry拷贝到新的桶中。
此过程会有较大的资源消耗。
为了避免在向HashMap中put过多元素时频繁发生扩容,需要指定一个合适大小的初始化容量。

初始化容量设置多少合适

我们的目的是避免频繁发生扩容,所以一次性指定初始化容量合适大小。
并不是我要塞入多少个元素,就初始化为多少,来看一个例子:
当我要向HashMap中put 7个元素,我将HashMap初始化为7

HashMap map = new HashMap(7);

因为传入的是一个7,HashMap会选择一个大于等于该值的2的次幂作为容量capacity
根据loadFactory=0.75,计算出threshold = 8 * 0.75 = 6
当我们put第7个元素时,就会触发扩容,扩容到16。
但是我们就想put 7个元素,在第7个时触发扩容,导致了性能和空间的浪费。
所以,我们在初始化时,选择多少的初始化容量,值得考虑。

借鉴HashMap 中putAll()方法,得出以下公式:

initCapatity = expectedSize / 0.75F + 1.0F 

这个计算方法虽然浪费了一些空间,但是在性能上却提升了。
当我们要put 7个元素, initCapacity = 7 / 0.75 + 1 = 10 ,经过HashMap的计算,第一个大于10的2的次幂的数是16,所以HashMap的容量是16。
16 * 0.75 = 12 ,当我要put第7个元素时,并不会发生扩容,性能得到提升。
而且与第一次我们设置初始化容量为7相比,最终所占用的空间是一样的。

为什么选择2的次幂作为容量

因为,在put时,HashMap会根据key的hash来计算对应Entry所在桶的位置。
通常都是利用求余来实现的

index = hash % capacity

但是直接利用%来计算是非常慢的,效率非常第低。
我们知道,在计算机中,最快的运算就是位运算了。
存在这样一个规律:当capacity是2的次幂时,满足以下恒等式

hash % capacity == hash & (capacity - 1)

在我们创建HashMap时,就做了第一步处理,选择大于等于给定数字的第一个2的次幂作为容量,就保证了。

http://www.lryc.cn/news/63099.html

相关文章:

  • [jenkins自动化2]: linux自动化部署方式之流水线(下篇)
  • idea使用 ( 二 ) 创建java项目
  • RabbitMq-接收消息+redis消费者重复接收
  • Orangepi Zero2 全志H616简介
  • Golang每日一练(leetDay0047)
  • HCL Nomad Web 1.0.7发布和新功能验证
  • 春招网申简历填写三技巧
  • 计算机网络基础知识总结
  • (下)苹果有开源,但又怎样呢?
  • row_number 和 cte 使用实例:考场监考安排
  • 2023天梯赛记录
  • 被吐槽 GitHub仓 库太大,直接 600M 瘦身到 6M,这下舒服了
  • OpenGL(三)——着色器
  • 【MySQL】单表查询
  • 第一章 安装Unity
  • 20230425----重返学习-vue项目-vue自定义指令-vue-cli的配置
  • el-input 只能输入整数(包括正数、负数、0)或者只能输入整数(包括正数、负数、0)和小数
  • Docker Compose的常用命令与docker-compose.yml脚本属性配置
  • with语句和上下文管理器(py编程)
  • 《JavaEE初阶》HTTP协议和HTTPS
  • 微信小程序 | 基于高德地图+ChatGPT实现旅游规划小程序
  • Excel技能之实用技巧,高手私藏
  • 黑马程序员Java零基础视频教程笔记-运算符
  • Microsoft Data Loss Prevention(DLP)部署方案
  • win系统使用frp端口映射实现内网穿透,配置“任务计划程序”提高稳定性
  • python工具方法 39 大图裁剪为小图|小图还原成大图(含生成大图伪标签)
  • MUSIC算法仿真
  • redis 数据类型详解 以及 redis适用场景场合
  • python基于轻量级YOLOv5的生猪检测+状态识别分析系统
  • 阅读笔记 First Order Motion Model for Image Animation