要明白为什么是2的n次幂,这要从hashmap的hash方式说起,hashmap的容量期望就是用来均匀散列存放map中的元素。hashmap根据hash值把元素放到hashmap内部数组的一个位置上。

1、为什么hashmap的容量必须是2的n次幂??

我们不妨先看看hashCode的原理,以String为列,获取hashCode的方法源码

    public int hashCode() {int h = hash;if (h == 0 && value.length > 0) {char val[] = value;for (int i = 0; i < value.length; i++) {h = 31 * h + val[i];}hash = h;}return h;}

可以看出任何对象的hashCode方法返回一个对应的int类型的结果,所以hashCode的取值范围是-2^31 ~ (2^31-1),即-2147483648~2147483647。

(值得注意的是:两个对象equals相等,hashCode一定相等,hashCode相等,equals不一定相等,从上段代码hashCode源码中可以得出这个结论,会存在不同的数,计算出相同的hashCode)

当要存入map中的条目数为n(0 < n <= 1073741824)的时候,这n个数要散列分布到map数组中,那么map数组的长度为多少,才能使得元素在map数组最为呈现散列均匀分布,又不浪费空间呢?那么就是要求每个数通过某种算法,填入到数组表中的概率是相等的。这种算法最容易想到的就是取模运算。

我们联想下通过运算能不能达到类似效果呢,因为计算机本身就是通过位运算完成所有计算的,通过测试发现位运算比模运算快约27倍。怎么个位运算,使得n个元素能在有指定数组长度的情况下,得到散列分布索引值呢?

参考hashMap获取索引的算法源码

    public static int indexFor(int h, int length) {return h & (length-1);}

indexFor中的h是hashCode通过变换之后的值。怎么变换呢?为什么要变换呢?hashCode结果是一个32位的二进制数,如果直接用如此长的二进制数和目标length-1直接进行与运算结果为怎么样呢?答案是高位(也就是左边的位)会大量丢失。看看下面例子

如果两个数的hashCode分别是

11001110 11001101 11011101 00111110

11001110 11001111 11011101 00111110

只有其中第15位不一样(任何高位不一样都可以,假如我们以16位为划分,任何两个高16位不一样,低16位一样的数),这两个hashCode与length-1做与运算,当length<2的16次方时(1<<16 = 65536)得到两个hashCode & length-1 的结果一样,这样的两个数,却产生了相同的hash结果,于是hashMap想到了一种处理方式:将hashCode的高16位于低16位进行异或运算,其实吧用普通话说就是把高16位的特征放到低16位中,让低低16位同时拥有了高16的特征。这样,在与length-1做与运算时结果就不一样了。其计算过程如下:假如有hashCode a=11001110 11001111 11011101 00111110

int a =  11001110 11001111 11011101 00111110a >>> 16 = 00000000 00000000 11001110 11001111a ^ (a >>> 16)11001110 11001111 11011101 00111110
00000000 00000000 11001110 11001111
=
11001110 11001111 00010011 11110001

用java代码表达

static final int hash(Object key) {int h;return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);}

hashCode结果是32位int 结果,将32位的结果做变换,可以从下面例子来研究下

int a =  11001110 11001111 11011101 00111110a >>> 16 = 00000000 00000000 11001110 11001111a ^ (a >>> 16)11001110 11001111 11011101 00111110
00000000 00000000 11001110 11001111
=
11001110 11001111 00010011 11110001

在算法上可以看出,32位hashcode中保持高16位不变,高16与低16异或结果作为新的低16位。然后用hash得到的结果传入方法indexFor获取到hashMap的索引。

hash与n-1 做与运算,这样既在容量远小于1<<16(往往hashmap的容量远小于65535) 的情况下,hash & (n-1),在计算中只有低位((n-1)对应的二进制数相同的位数)参与&运算,计算效率高,同时也保证的hash的高16位参与了索引运算。这样得到的索引能呈较为理想的散列分布,在将条目放入hashMap中时,最大限度避免hash碰撞。

设hashMap容量为16,那么我们看下计算出来的索引值

11001110 11001111 00010011 11110001
&
00000000 00000000 00000000 00001111
=
00000000 00000000 00000000 00000001

索引=1,将该条目存入hash表标号为1的位置处

回到标题中的问题:为什么容量一定要是2的n次幂,只有当length=2的n次幂的时候,length-1的二进制表达 全部为1(15的二进制1111,31的二进制位11111),只有当length-1的全部位都1时,h & (length-1)的结果才能均匀散列在数组中。这时,取模和与运算两种运算才是等价不等效的。至此就解释完了为什么hashMap的容量必须是2的整数次幂。

2、hashmap怎么去计算容量呢?怎么确保程序员传入的值是2的n次幂呢,答案是不能保证。那就只能通过内部算法hashMa自己来实现保证。

先不考虑扩容的情况下,通过传入我们自定义的容量值来构造HashMap实例

    public HashMap(int initialCapacity) {// DEFAULT_LOAD_FACTOR = 0.75this(initialCapacity, DEFAULT_LOAD_FACTOR);}

怎么保证传入的容量一定2的整数次幂,可以从源码来看,通过下面方法等到保证,结果一定是2的整数次幂,结果是大于等于initialCapacity的最小2的整数次幂。

n |= n >>> 1,  符号 | 为或运算,n 与 n>>>1 进行或运算,然后赋值给n

    static final int tableSizeFor(int cap) {int n = cap - 1;n |= n >>> 1;n |= n >>> 2;n |= n >>> 4;n |= n >>> 8;n |= n >>> 16;return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;}

当不指定Hashmap容量是,初始化默认容量为16,当往HashMap中put时,会检查当前hash表中的条目数是否大于容量的0.75倍,如果满足大于容量的0.75,hashMap调用resize方法,新容量 = 原来容量 x 2,在resize时将原来的容量翻倍,然后把之前所有条目复制一遍放入扩容之后的hash表中,这是非常好性能的操作,所以在已知容量数量的情况下避免扩容的情况发生。

为什么hashmap的容量必须是2的n次幂相关推荐

  1. HashMap的容量(桶的数量)为什么要是2的n次方

    2019独角兽企业重金招聘Python工程师标准>>> HashMap为了存取高效,要尽量较少碰撞,就是要尽量把数据分配均匀,每个链表长度大致相同. 关键就在于把当前数据存放到哪一个 ...

  2. hashmap应用场景_Java初学者进阶系列:HashMap的容量与性能

    1. HashMap的容量与性能 HashMap的性能受到两个参数的影响:初始化容量和负载因子,下面来详细讲述这几个关键问题. 1.1 Initial Capacity与Load Factor Ini ...

  3. hashmap 不释放空间_刁难问题,为什么HashMap默认容量为16加载因子为0.75

    前言:实际开发中我们大多数都是只能new HashMap<>来存储键值对,很少会去设置初始容量,虽然我们知道他的默认容量是16.但是在面试中,为了体现你个人好学的能力,还是会被经常问到为什 ...

  4. 关于HashMap初始化容量问题

    关于HashMap初始化容量问题 使用阿里云代码规范插件扫描后出现以下提示: hashmap should set a size when initalizing,即hashmap应该在初始化时设置一 ...

  5. HashMap的容量与扩容

    阅读JDK源码是提高我们技术很好的途径,其中HashMap的设计是非常优秀的,但是其中定义了许多变量,让人头昏目眩,那么这些变量都是什么意思呢 HashMap中的变量 本篇文章主要关注的字段是DEFA ...

  6. hashMap的容量(capacity)为什么必须是是2的n次方

    HashMap的容量(桶的数量)为什么要是2的n次方 查看hashmap的源码可以发现,如果new一个hashmap对象不指定容量(capacity)的话,hashmap的默认初始化容量是16,也就是 ...

  7. java HashMap 极限容量 大小限制 占用内存大小

    我很想知道HashMap到底极限容量是多少呢?搜了很久,没找到答案.也懒得继续找了~~~ 反正应该很大很大~~ 但HashMap占内存比较大,不少人都更关注于HashMap所占的内存怎么计算. 运行了 ...

  8. hashmap为什么容量是2的n次方

    我们知道在hashmap中要找到某个元素,需要根据key的hash值来求得对应数组中的位置.如何计算这个位置就是hash算法.前面说过hashmap的数据结构是数组和链表的结合,所以我们当然希望这个h ...

  9. 为什么哈希表的容量一定要是 2的整数次幂?

    首先,capacity 为 2的整数次幂的话,计算桶的位置 h&(length-1) 就相当于对 length 取模,提升了计算效率: 其次,capacity 为 2 的整数次幂的话,为偶数, ...

最新文章

  1. ubuntu14.04.03 vsftpd
  2. 5G NR — Massive MIMO 与波束赋形
  3. 深度学习核心技术精讲100篇(十二)-DCGAN(对抗生成网络)算法应用及代码实现
  4. 【每周NLP论文推荐】从预训练模型掌握NLP的基本发展脉络
  5. linux-内核启动流程分析
  6. python如何调用java写的接口_Python 调用翻译接口
  7. 图片预览------photoswipe 使用
  8. 微软Hololens学院教程- Holograms 101: Introduction with Device【微软教程已经更新,本文是老版本】...
  9. 计算机科学文章,计算机科学导论论文范文
  10. POJ 2115 模线性方程 ax=b(mod n)
  11. 你还认为中国没有桌面虚拟化核心技术?
  12. 连锁加盟网站源码_连锁60秒:招商只是开始,养商才最重要
  13. 【生活相关】北京南苑机场接人
  14. 雨滴桌面显示html,使用雨滴rainmeter打造炫酷桌面的方法!
  15. 如何修改论文,能够避开查重?
  16. c语言输入身高输出标准体重,项目2:就拿胖子说事---(4)计算出标准体重,输出体重状态(正常/超重/超轻)...
  17. 应急响应-记一次“完(sang)美(xin)无(bing)缺(kuang)”的SSH口令爆破
  18. HDU 6069 题解
  19. python爬取图片失败显示404_django使用图片延时加载引起后台404错误
  20. 高考数学95分能学计算机吗,你知道马云高考数学考了多少分吗?

热门文章

  1. 正规的伦敦金投资平台排行榜(top 10)
  2. 计算机word基础操作知识
  3. 吐血分享:QQ群霸屏技术教程之霸屏实施细则
  4. python 对中文文件求交集、并集、差集
  5. 电饭锅一会儿通电一会儿不通电【检修原因】
  6. UVA12096 - The SetStack Computer(set + map映射)
  7. 如何用一个makefile编译多个目标
  8. 【转】Java方向如何准备BAT技术面试答案(汇总版)
  9. Unity3d 动态字体
  10. 猿创征文 | MySQL从基础到高级