关于HashMap的一些疑问与解答

 

 

1.为什么TREEIFY_THRESHOLD要是8?

  treefy是有成本的,新增或删除元素时有额外的操作,同时TreeNode是普通Node体积的二倍,因而需要一个平衡点。
  随机hashcode下符合泊松分布,
  * 0: 0.60653066
  * 1: 0.30326533
  * 2: 0.07581633
  * 3: 0.01263606
  * 4: 0.00157952
  * 5: 0.00015795
  * 6: 0.00001316
  * 7: 0.00000094
  * 8: 0.00000006
  一个桶内8个元素的概率为一亿分之六;
  当情况发生时,说明hashcode设计的不合理,此时会有大量碰撞,这种情况下维护树就是有必要的。
  这个值越小,树化的影响就越强力,当小于8时,就可能导致频繁的树化,增加维护成本。

  普通Node内部维护的属性有:

    int hash,
    K key,
    V val,
    Node<K,V> next

  TreeNode维护的属性有:

      int hash,
      K key,
      V val,
      Node<K,V> next
      以及
      TreeNode<K,V> parent;
      TreeNode<K,V> left;
      TreeNode<K,V> right;
      TreeNode<K,V> prev;

  大约是普通Node大小的2倍

2.为什么put方法中的hash值要再进行一次加工,为何要使用 ( h = k.hashCode() ) ^ ( h >>> 16 ) 操作?

  计算桶位置的方法为 (tab.length - 1) & hash
  一般来说,hashmap的长度不会过大,不会超过65536(1 << 16),只有hash的低16(左右)位参与计算,如果直接将 k.hashCode() 当作hash进行位置计算的话,
  则其中的高16(左右)位信息并未参与计算,而 ( h = k.hashCode() ) ^ ( h >>> 16 ) 算法将高16位中的信息加以利用,得出低16位更加随机的hash,
  从而使桶的分布更佳均匀,使用^运算而不是&或者|,是因为^不会使结果有概率上的偏向性。
  tab.length固定为2的次方,二进制即为最高位有效位为1,
  其余位均为0的数字,tab.length - 1即为所有有效位均为1的二进制数字.
  hash对其求&,则获得的值x >= 0 且x <= tab.length - 1,共 tab.length 个,且分布完全依赖于hash的与tab.length - 1位数相同的低位。

3.扩容时扩大多少,为什么?
  容量扩大为原来的二倍( oldCap << 1 ),该方法配合寻桶算法 hash & (newCap - 1) 使用,
  使得扩容后元素在新数组中分布均匀(newCap - 1二进制最高有效位对应的hash相应的位如果为1,则桶位置下标为(原来的下表 + oldCap),若为0,则桶位置不变,
  0,1是依赖于hashcode的,随机的,所以理论上是均匀的)

 

posted @ 2021-11-21 21:15  陈月亮  阅读(41)  评论(0编辑  收藏  举报