HashMap中阈值的设计

HashMap中阈值的设计

    • 1.HashMap的长度为什么必须是2的次幂?
    • 2.加载因子为什么是0.75?
    • 3.处理冲突的几种方法
      • 4.equals()和hashCode()在HashMap中的重要性

1.HashMap的长度为什么必须是2的次幂?

为了实现一个尽量分布均匀的hash函数,利用的是Key值的HashCode来做某种运算。因此问题来了,如何进行计算,才能让这个hash函数尽量分布均匀呢?
首先想到的就是将Key值的HashCode值与HashMap的长度进行取模运算,即 index = HashCode(Key) % hashMap.length,但是,但是!这种取模方式运算固然简单,然而它的效率是很低的,浪费性能。所以jdk用了位运算的方式index = HashCode(Key) & (hashMap.length - 1);

举例:
“abc”十进制hashcode为96354,二进制为‭1 0111 1000 0110 0010‬,HashMap的默认长度为16,与15进的二进制1111位运算, 得到index = 2;
可以看出来,hash算法得到的index值完全取决与Key的HashCode的最后几位。这样做不但效果上等同于取模运算,而且大大提高了效率。

如果不是会怎样呢?
我们假设HaspMap的初始长度为10,要与9的二进制1001进行位运算。得到0,
但是 如果 hashCode为1 0111 1000 0110 1001 或者1 0111 1000 0110 1011这样会造成 有些索引出的值挤压太多。无法更好的分布均匀。

2.加载因子为什么是0.75?

加载因子过高,例如为1,虽然减少了空间开销,提高了空间利用率,但同时也增加了查询时间成本;

加载因子过低,例如0.5,虽然可以减少查询时间成本,但是空间利用率很低,同时提高了rehash操作的次数。

在设置初始容量时应该考虑到映射中所需的条目数及其加载因子,以便最大限度地减少rehash操作次数,所以,一般在使用HashMap时建议根据预估值设置初始容量,减少扩容操作。

选择0.75作为默认的加载因子,完全是时间和空间成本上寻求的一种折衷选择

3.处理冲突的几种方法

  1. 开放定址法(线性探测再散列,二次探测再散列,伪随机探测再散列)
  2. 再哈希法
  3. 链地址法
  4. 建立一个公共溢出区

hashmap出现了Hash冲突的时候采用第二种办法:链地址法。

4.equals()和hashCode()在HashMap中的重要性

使用HashMap,如果key是自定义的类,就必须重写hashcode()和equals()。

如果你重载了equals,比如说是基于对象的内容实现的,而保留hashCode的实现不变,那么很可能某两个对象明明是“相等”,而hashCode却不一样。
这样,当你用其中的一个作为键保存到hashMap中,再以“相等的”找另一个作为键值去查找他们的时候,则根本找不到。
对于每一个对象,通过其hashCode()方法可为其生成一个整形值(散列码),该整型值被处理后,将会作为数组下标,存放该对象所对应的Entry(存放该对象及其对应值)。
equals()方法则是在HashMap中插入值或查询时会使用到。当HashMap中插入值或查询值对应的散列码与数组中的散列码相等时,则会通过equals方法比较key值是否相等,
所以想以自建对象作为HashMap的key,必须重写该对象继承object的hashCode和equals方法。

你可能感兴趣的:(java)