为什么HashMap的大小都是2的幂次方
分为两个问题,一个是为什么创建HashMap时的容量都是2的幂次方,第二个是为什么扩容时是扩大两倍。 容量为2的幂次方 目的是为了更快的取模,假设容量为N,哈希值是hash,如果N是2的幂次方,则hash % N = hash & (N - 1) 2的幂次方的二进制表示,只有最高位是1,其他位都是0,2的幂次方减一的二进制表示则全部是1。 hash & (N -1)本质上是只保留hahs的末尾几位,得到的结果一定小于N, 在0到N-1之间。 扩容扩大两倍 扩大两倍可以保证原有的元素要么保持在原来的索引上,要么移动到原索引加N的位置,可以保证数据在扩张后的分布不会变得更差。 首先map的容量都是2的幂次方,那么扩大两倍,本质上就是在取模的时候多保留一位,那么原来的哈希值如果上一位是0,则扩容后取模的结果不会变,如果上一位是1,那么扩容后取模的结果就是二进制数最前面加上1 在二进制最高位+1,相当于十进制里+N,比如从16扩大到32,那么相当于本来取4位,变成取5位,取5位相当于在原来的索引上+16 举个例子: hash值是110010,此时map的大小是16,那么取后4位,0010做索引,也就是2 扩大两倍后(总大小是32),取后5位,也就是10010做索引,结果是18 扩大两倍后会不会有在扩容前没有hash冲突的两个hash值,扩容后hash冲突了 按照上文的结论,扩容后,原来hash值要么还在原来的索引上,要么在原来的索引+N的位置上(N是扩容前的大小) 两个索引(在扩容前不冲突)在扩容后只可能有3种情况: 两个索引位置都不变,那么扩容后也不会有冲突 两个索引的位置都+N,那么也不会有冲突 一个索引变化,另一个不变化 第三种情况,有没有可能出现,扩容前不冲突,扩容后冲突的情况。因为扩容后,索引要么不变,要么+N,如果想要扩容后发生冲突,那么需要两个hash值,为hash1,hash2 假设 hash1与hash2在扩容前不冲突,扩容后冲突。 则一定有: hash1在扩容后+N,hash2扩容后不移动 扩容前hash2的索引正好比hash1大N 扩容前取模运算保留低位的n位,而扩容后保留n+1位, 因为hash1在扩容后+N,说明hash1的第n+1位是1,而hash2扩容后不移动,说明hash2的第n+1位是0, 扩容前hash2的索引正好比hash1大N, 就需要hash2的索引的低n-1位和hash1的索引的低n-1位相同。并且hash2的索引的第n位是1,hash1的索引的第n位是0 那么我们会得到 n+1 n 相同的低位 hash1: 1 0 XXX hash2: 0 1 XXX 因为hash1和hash2的低n+1位不完全一致,所以扩容后取n位hash1和hash2不可能落到一个索引上,假设不成立。