Java编程:浅析 HashMap 中数组的 size 为什么必须是 2 的整数次幂
2017-08-31 18:34
736 查看
直入主题:HashMap 中数组的 size 必须是 2 的幂,是为了将 key 的 hash 值均匀的分布在数组的索引上,下面我们来进行分析。如有不正之处,欢迎批评指正。
HashMap 中使用 indexFor 方法来计算 key 所在的数组的索引,实现逻辑为 key 的 hash 值与数组的长度值减 1 进行与运算,代码如下:
我们举两个例子来说明原因。
例一: 假定 length = 50 (非 2 的整数次幂),二进制值为 0011 0010,这里我们使用 8 位二进制数来进行计算。length - 1 = 49,二进制值为 0011 0001。我们计算任何整数与 49 进行与运算的可能的结果如下:
可能的结果值为:0、1、16、17、32、33、48、49,对于一个长度为 50 的数组,我们只命中了其中的 8 个索引值,产生了索引值的非均匀分布。
例二: 假定 length = 32(2的五次幂),二进制值为 0010 0000,这里我们使用 8 位二进制数来进行计算。length - 1 = 31,二进制值为 0001 1111。我们计算任何整数与 31 进行与运算的可能的结果如下:
可能的结果值为:0 到 31,对于一个长度为 32 的数组可能的索引值范围也刚好是 0 到 31,以此类推,当 HashMap 中数组的 size 为 2 的整数次幂时,可以保证 key 的 hash 值被均匀的分布到数组上。因此建议大家尽量将 HashMap 的 initialCapacity 值设置为 2 的整数次幂。其实,在 HashMap 的源码中已经为做了优化处理,代码如下:
其中如下代码保证 capacity 的值为 2 的整数次幂,保证了 key 索引值的均匀分布。
HashMap 中使用 indexFor 方法来计算 key 所在的数组的索引,实现逻辑为 key 的 hash 值与数组的长度值减 1 进行与运算,代码如下:
/** * Returns index for hash code h. */ static int indexFor(int h, int length) { return h & (length - 1); }
我们举两个例子来说明原因。
例一: 假定 length = 50 (非 2 的整数次幂),二进制值为 0011 0010,这里我们使用 8 位二进制数来进行计算。length - 1 = 49,二进制值为 0011 0001。我们计算任何整数与 49 进行与运算的可能的结果如下:
0000 0000 //0 0000 0001 //1 0001 0000 //16 0001 0001 //17 0010 0000 //32 0010 0001 //33 0011 0000 //48 0011 0001 //49
可能的结果值为:0、1、16、17、32、33、48、49,对于一个长度为 50 的数组,我们只命中了其中的 8 个索引值,产生了索引值的非均匀分布。
例二: 假定 length = 32(2的五次幂),二进制值为 0010 0000,这里我们使用 8 位二进制数来进行计算。length - 1 = 31,二进制值为 0001 1111。我们计算任何整数与 31 进行与运算的可能的结果如下:
0000 0000 //0 0000 0001 //1 0000 0010 //2 0000 0011 //3 0000 0100 //4 0000 0101 //5 0000 0110 //6 0000 0111 //7 0000 1000 //8 0000 1001 //9 0000 1010 //10 0000 1011 //11 0000 1100 //12 0000 1101 //13 0000 1110 //14 0000 1111 //15 0001 0000 //16 0001 0001 //17 0001 0010 //18 0001 0011 //19 0001 0100 //20 0001 0101 //21 0001 0110 //22 0001 0111 //23 0001 1000 //24 0001 1001 //25 0001 1010 //26 0001 1011 //27 0001 1100 //28 0001 1101 //29 0001 1110 //30 0001 1111 //31
可能的结果值为:0 到 31,对于一个长度为 32 的数组可能的索引值范围也刚好是 0 到 31,以此类推,当 HashMap 中数组的 size 为 2 的整数次幂时,可以保证 key 的 hash 值被均匀的分布到数组上。因此建议大家尽量将 HashMap 的 initialCapacity 值设置为 2 的整数次幂。其实,在 HashMap 的源码中已经为做了优化处理,代码如下:
public HashMap(int initialCapacity, float loadFactor) { if (initialCapacity < 0) throw new IllegalArgumentException("Illegal initial capacity: " + initialCapacity); if (initialCapacity > MAXIMUM_CAPACITY) initialCapacity = MAXIMUM_CAPACITY; if (loadFactor <= 0 || Float.isNaN(loadFactor)) throw new IllegalArgumentException("Illegal load factor: " + loadFactor); // Find a power of 2 >= initialCapacity int capacity = 1; while (capacity < initialCapacity) capacity <<= 1; this.loadFactor = loadFactor; threshold = (int)Math.min(capacity * loadFactor, MAXIMUM_CAPACITY + 1); table = new Entry[capacity]; useAltHashing = sun.misc.VM.isBooted() && (capacity >= Holder.ALTERNATIVE_HASHING_THRESHOLD); init(); }
其中如下代码保证 capacity 的值为 2 的整数次幂,保证了 key 索引值的均匀分布。
// Find a power of 2 >= initialCapacity int capacity = 1; while (capacity < initialCapacity) capacity <<= 1;
相关文章推荐
- 什么是 哈希表 HashMap 中数组的 size 为什么必须是 2 的整数次幂
- 知道为啥HashMap里面的数组size必须是2的次幂?
- 知道为啥HashMap里面的数组size必须是2的次幂?
- NanguoCoffee 知道为啥HashMap里面的数组size必须是2的次幂?
- 知道为啥HashMap里面的数组size必须是2的次幂?
- randnumber_mn.c 生成一个数组,包括k个不重复的整数,并且要求这些整数范围为[m,n),生成的结果中不能包含inum中的数字,size表示inum的长度
- 对于一个SIZE大小的数组,元素是[0,SIZE-1]区间内的整数,判断其中是否有重复元素
- 题目:返回一个整数数组中最大子数组的和。(要求程序必须能处理1000 个元素)
- 给定按升序排序的整数数组,找到给定目标值的起始和终止位置。 您的算法的运行时复杂度必须是O(log n)的顺序。
- HashMap为什么初始容量为2的次幂
- HashMap中的为什么hash的长度为2的幂而&位必须为奇数
- HashMap为什么线程不安全?浅析高并发情况下的HashMap
- 为什么多维数组必须声明列数,而对行数没有要求???
- 为什么数组的BUFFSIZE要定义为4096比较好?
- 为什么在把多维数组传递给函数时必须要指定除第一维之外的其他维
- 为什么数组可以不加“&”取它的地址,而普通变量必须要加“& ”才可以呢?
- 为什么数组可以不加“&”取它的地址,而普通变量必须要加“& ”才可以呢?
- 深入理解hashmap集合的底层数组长度为什么一定为2的幂次
- 线程数组HashMap为什么是线程不安全的?
- C语言问题:为什么可以留着数组中第一维的参数不进行说明,但是其他维数必须说明呢?