为什么在HashMap中使用hash方法

时间:2014-04-08 11:28:25

标签: java hashmap

hash方法的Java文档状态,

  

检索对象哈希码并将补充哈希函数应用于结果哈希,以防止质量差的哈希函数。这很关键,因为HashMap使用两个幂的长度哈希表,否则会遇到低位不同的hashCodes的冲突。

我无法理解的是,

1)为什么HashMap使用两个长度的哈希表

在声明表时也说明了这一点:

/**
 * The table, resized as necessary. Length MUST Always be a power of two.
 */
transient Entry<K,V>[] table;

为什么会出现这种限制?

2)否则会遇到低位不同的hashCodes的冲突。是什么意思?

2 个答案:

答案 0 :(得分:3)

散列映射的目的是在搜索特定密钥时非常快速地缩小需要查看的对象数(理想情况下为0或1)。

HashMap.get(key)的一般方法如下:

  1. 调用key.hashCode()获取表示对象的单个整数。

  2. 查看哈希&#34;桶&#34;基于该哈希码,可以包含零个或多个条目。

  3. 浏览广告管理系统中的每个条目,查找是否有任何条目的密钥为.equals(key)。如果是这样,请将其退回。如果存储桶中的条目没有与搜索的条目具有相同的密钥,则返回null。

  4. good hashmap和 bad hashmap之间的区别在于速度。你必须平衡所有这三个问题:

    1. 您可以多快将密钥转换为哈希码?

    2. 两个不同的密钥多长时间映射到同一个哈希码?

    3. 您多久会将两个带有不同哈希码的密钥放入同一个&#34;桶&#34;?

    4. Java的设计师选择了一组他们认为最佳平衡的权衡。没有正确的答案,但您必须选择一种特定的方法,并将文档写入文档。

      Java的设计人员可能会根据添加到哈希图中的典型数据获得一些统计证据。

      他们选择通过提取哈希码的最低 n 位来将哈希码转换为桶,因为这些位的变化频率高于高位。他们选择通过另一种将哈希码转换为桶的典型方法(在除以素数后的整数余数)来提取比特,因为它通常是Java最常部署的平台上的更快操作。

      Java的设计人员可能发现,第1步是hashCode()的实现,是由Java用户编写的,并且通常很糟糕,为他们想要的大量对象返回相同的hashCode存储在同一个hashmap中。想象一下,如果hashCode是这样的:

      public class MyInteger {
          final int i;
          public MyInteger(int i) {
              this.i = i;
          }
          public int hashCode() {
              return i << 24; // will return 0x00000000, 0x01000000, etc.
          }
          public boolean equals(Object o) {
              return (o != null) && (o instanceof MyInteger) && ((MyInteger)o).i == i;
          }
      }
      

      这就是他们所说的&#34;质量差&#34 ;;哈希码的低位不会发生很大变化。在这种病态实施中,低24位根本不变!

      在这种情况下,对于小于16,777,216个桶的哈希映射,可以进入hashmap的每个密钥都将转到bucket 0。其他16,777,215个桶将为空。

      其他人的哈希码可能没有这么糟糕,但它们已经足够糟糕以至于Java的设计师选择添加第二个哈希码来帮助提高两个不同密钥的可能性分成两个不同的桶,从而减少每次检索给定密钥时需要检查的对等数量。

答案 1 :(得分:0)

当HashMap需要调整大小时会创建一个新的存储区数组,这些存储区是使用hashCode()访问的(使用较小的附加操作将int hashCode映射到hashMap中的存储区数量。)登记/> 这个数组的2个大小的功能允许int hashCode到桶号的一些巧妙映射 - 基本上只使用hashCode的下半部分(屏蔽较高部分)来解决桶。