2、HashMap中的常量( 二 )


问题二:为什么初始化大小是16呢?
首先需要明确:的容量只能是2的幂次方,扩容的策略也是原来的容量 * 2,就算构造函数传入初始容量不是2的幂次方,构造函数中也会将传入的初始容量转为第一个 > = 的2的幂作为容量 。
为什么容量要求是一个2的幂呢?
这是为了方便计算!当获取到一个key的hash值后,我们需要将hash值映射到数组中,常规的思路就是将 hash%n 获取桶数组的索引,但是取余操作需要消耗大量的时间,如果能够使用&操作替代%操作就能加快计算 。因此下标计算使用以下式子:
int index=key.()&(n-1) 获取桶中的下标 。而只有当n为2的幂,(n-1)&key.() 才能使hash值在数组下标中均匀分配,获得的index就能减少重复,这样就能减少冲突和提高的查找效率 。
那么为什么是16呢?32、8就不行吗?
因为是8的话很容易导致map扩容影响性能,如果分配的太大的话又会浪费资源,所以就使用16作为初始大小 。
3、的构造函数
//默认无参构造,指定一个默认的加载因子public HashMap() {this.loadFactor = DEFAULT_LOAD_FACTOR; }//可指定容量的有参构造,但是需要注意当前我们指定的容量并不一定就是实际的容量,下面会说public HashMap(int initialCapacity) {//同样使用默认加载因子this(initialCapacity, DEFAULT_LOAD_FACTOR);}//可指定容量和加载因子,但是笔者不建议自己手动指定非0.75的加载因子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);this.loadFactor = loadFactor;//这里就是把我们指定的容量改为一个大于它的的最小的2次幂值,如传过来的容量是14,则返回16//注意这里,按理说返回的值应该赋值给 capacity,即保证数组容量总是2的n次幂,为什么这里赋值给了 threshold 呢?//先卖个关子,等到 resize 的时候再说this.threshold = tableSizeFor(initialCapacity);}//可传入一个已有的mappublic HashMap(Map m) {this.loadFactor = DEFAULT_LOAD_FACTOR;putMapEntries(m, false);}//把传入的map里边的元素都加载到当前mapfinal void putMapEntries(Map m, boolean evict) {int s = m.size();if (s > 0) {if (table == null) { // pre-sizefloat ft = ((float)s / loadFactor) + 1.0F;int t = ((ft < (float)MAXIMUM_CAPACITY) ?(int)ft : MAXIMUM_CAPACITY);if (t > threshold)threshold = tableSizeFor(t);}else if (s > threshold)resize();for (Map.Entry e : m.entrySet()) {K key = e.getKey();V value = http://www.kingceram.com/post/e.getValue();//put方法的具体实现,后边讲putVal(hash(key), key, value, false, evict);}}}
需要注意的是:
()

2、HashMap中的常量

文章插图
上边的第三个构造函数中,调用了方法,这个方法是怎么实现的呢?
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;}
我们以传入参数为14 来举例,计算这个过程 。i
首先,14传进去之后先减1,n此时为13 。然后是一系列的无符号右移运算>>> 。
//13的二进制0000 0000 0000 0000 0000 0000 0000 1101 //无右移1位,高位补00000 0000 0000 0000 0000 0000 0000 0110 //然后把它和原来的13做或运算得到,此时的n值0000 0000 0000 0000 0000 0000 0000 1111 //再以上边的值,右移2位0000 0000 0000 0000 0000 0000 0000 0011//然后和第一次或运算之后的 n 值再做或运算,此时得到的n值0000 0000 0000 0000 0000 0000 0000 1111...//我们会发现,再执行右移 4,8,16位,同样n的值不变//当n小于0时,返回1,否则判断是否大于最大容量,是的话返回最大容量,否则返回 n+1return (n