为什么 HashMap 的容量大小要设置为2的N次方?
原文链接:https://www.changxuan.top/?p=1208
前两天,我在一位同学提交中看到了下面这样的一行代码,让我很是惊讶。
Map<String, String> temp = new HashMap<>(6);
我给他说,你这样实例化 Map 对象不好用,他不服气。我说小朋友:如果想指定 HashMap 对象的容量得用2的N次方。他说你这也没用。我说,我这个有用,这样才能充分利用分配的内存空间。他非和我试试,我说可以,不过得先一起看看源码。
什么是HashMap?
在弄懂标题的问题之前,首先需要清楚 HashMap 的概念。HashMap 是基于哈希表的 Map 接口的实现,线程不安全,且不保证映射顺序。
HashMap 存储数据依赖的是数组和[链表|红黑树],具体链表和红黑树之间如何转换的细节此文不做详细介绍。而本文开头提到的实例化容量大小指的则是数组的大小。
如何计算元素在数组中所对应的下标?
首先计算元素的哈希值,方法如下:
static final int hash(Object key) {
int h;
// h = key.hashCode();
// h = h ^ (h >>> 16)
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
为什么不直接使用 key.hashCode()的值,我们后面会提到。
计算出来哈希值后,由于数组容量相对来说较小肯定不能直接使用哈希值当作索引值。所以需要使用哈希值对数组长度减一后的值取模。不过在在 HashMap 中可不是直接使用 % 运算符来操作的。为了提高效率,采用的是与运算的方式,代码如下:
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
boolean evict) {
Node<K,V>[] tab; Node<K,V> p; int n, i;
if ((tab = table) == null || (n = tab.length) == 0)
n = (tab = resize()).length;
// n 为数组容量, (n-1) & hash 则是计算索引值
if ((p = tab[i = (n - 1) & hash]) == null)
tab[i] = newNode(hash, key, value, null);
else {
... ...
}
}
既然清楚了计算元算在数组中所对应下标的方法,那么证明为什么实例化 HashMap 对象的容量要使用2的N次方就简单多了。
假如初始容量为2的3次方数字8,当哈希值与容量大小减一的值进行与运算时可以保证结果比较均匀的分布在数组上。
10100101 11000100 00100101
& 00000000 00000000 00000111 // 7
----------------------------------
00000000 00000000 00000101 // 结果可以是[0,7]中的任一数字
如果初始容量为6,那么出现哈希冲突的几率就会增加了。
10100101 11000100 00100101
& 00000000 00000000 00000101 // 5
----------------------------------
00000000 00000000 00000101 // 5
10100101 11000100 00100111
& 00000000 00000000 00000101 // 5
----------------------------------
00000000 00000000 00000101 // 5
如果下面的值低位全是1,那么上面的这次哈希冲突则可以避免。那么你想想,假如指定的容量大小为5又会怎么样呢?其实2的N次方数字-1的二进制形式这个特性在好多地方会很好用,可以在小本本记上。
哦,前面说为什么计算出来的散列值需要再让高16位和低十六位做异或运算,主要是让参与与运算的位同时具有高位和低位的特征,来减少哈希碰撞次数。
小朋友,还试不试啦!
为什么 HashMap 的容量大小要设置为2的N次方?的更多相关文章
- HashMap的容量大小增长原理(JDK1.6/1.7/1.8)
. 前言 HashMap的容量大小会根据其存储数据的数量多少而自动扩充,即当HashMap存储数据的数量到达一个阈值(threshold)时,再往里面增加数据,便可能会扩充HashMap的容量. 可能 ...
- 关于HashMap初始化容量问题
使用阿里云代码规范插件扫描后出现以下提示: hashmap should set a size when initalizing,即hashmap应该在初始化时设置一个大小 在网上搜到一篇讲解(htt ...
- 我说我了解集合类,面试官竟然问我为啥HashMap的负载因子不设置成1!?
在Java基础中,集合类是很关键的一块知识点,也是日常开发的时候经常会用到的.比如List.Map这些在代码中也是很常见的. 个人认为,关于HashMap的实现,JDK的工程师其实是做了很多优化的,要 ...
- HashMap初始化容量过程
集合是Java开发日常开发中经常会使用到的,而作为一种典型的K-V结构的数据结构,HashMap对于Java开发者一定不陌生.在日常开发中,我们经常会像如下方式以下创建一个HashMap: Map&l ...
- 我说HashMap初始容量是16,面试官让我回去等通知
众所周知HashMap是工作和面试中最常遇到的数据类型,但很多人对HashMap的知识止步于会用的程度,对它的底层实现原理一知半解,了解过很多HashMap的知识点,却都是散乱不成体系,今天一灯带你一 ...
- 【转载】VMware虚拟机修改硬盘容量大小
很多人在安装虚拟机系统的时候,为了节省硬盘空间,把硬盘容量设置得较小,可是后来发现硬盘容量不够用了.在VMware中又不能直接修改虚拟机的硬盘容量大小,或者重建虚拟机系统,非常麻烦. 其实在VMwar ...
- jdk1.8 HashMap底层数据结构:深入解析为什么jdk1.8 HashMap的容量一定要是2的n次幂
前言 1.本文根据jdk1.8源码来分析HashMap的容量取值问题: 2.本文有做 jdk1.8 HashMap.resize()扩容方法的源码解析:见下文“一.3.扩容:同样需要保证扩容后的容量是 ...
- 为什么jdk1.8 HashMap的容量一定要是2的n次幂
一.jdk1.8中,对“HashMap的容量一定要是2的n次幂”做了严格控制 1.默认初始容量: [Java] 纯文本查看 复制代码 ? 1 2 3 4 /** * The default init ...
- 根据屏幕大小动态设置字体rem
1.根据屏幕大小动态设置字体rem var docEl = document.documentElement, //当设备的方向变化(设备横向持或纵向持)此事件被触发.绑定此事件时, //注意现在当浏 ...
随机推荐
- 在线思维导图Ayoa可以用来梳理双十一优惠规则哦
一年一度的双十一又要来了,小伙伴们是否准备好开始买买买了呢?今年双十一,遇上英雄联盟S10总决赛,1/4决赛苏宁对上京东也让这个"电商大战"产生了很多有趣的梗.当然在玩梗的同时,广 ...
- EasyRecovery帮您轻松拯救办公室断电后的文件丢失
故事要从半个月前说起,某天中午,社畜小编得到了上头的传令,要为即将到来的双十一狂欢节写一个活动策划案. 想着时间也不是很充裕,还要留一些时间修修补补,于是小编连续三天挑灯夜战,终于在某天周五的晚上把策 ...
- centons 7 清机 脚本
#/bin/bash##################################初始化系统###################setenforce 0 yum install -y yum- ...
- P3619 魔法
考虑两个任务 \(1\) 和 \(2\),当前时间为 \(T\),两个任务都要完成. 先完成任务 \(1\) 的条件是 \(T>t_1\) 且 \(T+b_1>t_2\),先完成任务 \( ...
- Java蓝桥杯练习题——求小数n位后3个数
求整数除法小数点后第n位开始的3位数 位数不足的补0,如0.125小数第3位后三位:0.12500→500 输入格式:a b n,空格分开,a是被除数,b是除数,n是小数后的位置 输出格式:3位数字, ...
- docker安装myInfluxDB映射本地目录+开机后台自启动
CentOS7环境 1.docker hup库搜索influxdb docker search influxdb 2.拉取influxdb镜像 docker pull influxdb 3.查看已下载 ...
- 通过 GraalVM 将 Java 程序编译成本地机器码!
前言 2018年4月,Oracle Labs新公开了一项黑科技:Graal VM. 这是一个在HotSpot虚拟机基础上增强而成的跨语言全栈虚拟机,可以作为"任何语言"的运行平台使 ...
- JZOJ2020年8月10日提高组T3 玩诈欺的小杉
JZOJ2020年8月10日提高组T3 玩诈欺的小杉 题目 Description 是这样的,在小杉的面前有一个N行M列的棋盘,棋盘上有\(N*M\)个有黑白棋的棋子(一面为黑,一面为白),一开始都是 ...
- js实现视频截图,视频批量截图,canvas实现
截取视频的某一时间的图像并保存 利用canvas的绘画能力画出视频某一帧的视频画面, 获得到图像之后转换成base64图像, 再利用a标签的实现自动保存到本地 html代码 <!DOCTYPE ...
- charles功能(一)修改request请求参数
1.接口处 鼠标右击,选择breakpoints(允许本接口使用breakpionts功能) 2.开始设置断点值 3.然后修改这一条 4.然后执行 5.最终结果