比较hashCode工具

时间:2018-05-09 20:43:01

标签: java hash

我有一个任务是使用定义在java中实现字符串的哈希码。我写了这段代码。

   public int hash(String str) {
        int hashValue = 0;
        int power;
        for (int i = 0; i < str.length(); i++) {
            power = (str.length() -1 - i);
            hashValue = hashValue + str.charAt(i) * (int) Math.pow(31, power);
        }
        return hashValue;
    }

我发现我的方法中的结果与hashcode()的结果仅与长度小于8的字符串相同。这应该是那样的,还是我的方法不准确?我已经看到,可能是字符串超过8个字符的哈希码已经改变了。

2 个答案:

答案 0 :(得分:2)

查看jdk中的hashCode实现:

public static int hashCode(byte[] value) {
    int h = 0;
    int length = value.length >> 1;
    for (int i = 0; i < length; i++) {
        h = 31 * h + getChar(value, i);
    }
    return h;
}

可能会发生,您的方法产生的结果与此方法相同。实际上,没关系。它只是一种散列方法 注意,该散列方法不需要“准确”。这是一种将任意对象(字符串)减少为int的方法。您可以使用任何您想要的方法。

答案 1 :(得分:1)

您对字符串的哈希代码的实现类似于Java的String类的hashCode实现,但由于Java缩小double返回的微妙方式,它并不完全相同Math.powint

对于字符串"abcdefg",长度为7个字符,您的方法和Java的方法一致 - 它们都返回-1206291356。对于字符串"abcdefgh",长度为8个字符,您的方法和Java的方法不一致 - 您的返回值为1858279332,而Java的方法返回1259673732。

首先,我们来介绍它们相似的方式。以下是Java 8's code from Grepcode供参考:

public int More ...hashCode() {
    int h = hash;
    if (h == 0 && value.length > 0) {
        char val[] = value;

        for (int i = 0; i < value.length; i++) {
            h = 31 * h + val[i];
        }
        hash = h;
    }
    return h;
}

每次循环发生时,String的Java实现都会乘以31因子。实际上,每个角色都有31的力量。

您的实施尝试使用Math.pow, which returns a double直接确定31乘以字符值的能力。然后你把它转回int,因为这就是哈希码的类型。

现在,让我们讨论细微差别。

Java String hashCode实现仅增加并添加int s - 即使发生溢出,它也会int溢出,在此期间保留低32位信息

对于Math.pow的实施,JLS, Section 5.1.3涵盖了将double转换为int时发生的原始缩小转化。

  

将浮点数转换为整数类型T需要两个步骤:

     
      
  1. 在第一步中,浮点数转换为long(如果T为long)或转换为int(如果T为byte,short,char或int),如下所示:

         
        
    • 如果浮点数为NaN(§4.2.3),则转换的第一步结果为int或long 0。

    •   
    • 否则,如果浮点数不是无穷大,则浮点值将四舍五入为整数值V,使用IEEE 754舍入为零的模式舍入为零(第4.2.3节) 。然后有两种情况:

    •   
  2.         

    一个。如果T很长,并且这个整数值可以表示为long,那么第一步的结果就是长值V.

         

    湾否则,如果此整数值可以表示为int,则第一步的结果是int值V.

         
        
    • 否则,以下两种情况之一必须为真:
    •   
         

    一个。该值必须太小(大幅度或负无穷大的负值),第一步的结果是int或long类型的最小可表示值。

         

    湾该值必须太大(大幅度或正无穷大的正值),第一步的结果是类型为int或long 的最大可表示值。

(大胆强调我的)

如果您有一个7个字符的字符串,则计算31 6 ,即887,503,681,仍然可以表示为int。但是,如果你有一个8个字符的字符串,你计算31 7 ,即27,512,614,111,并且它太大而不适合int - int的最大值是大约20亿。缩小转换将其转换为最大整数值,即2,147,483,647。此时,您使用的值与Java的String hashCode方法有效使用的值不同。真正答案的低32位是 not 保留在您的方法中,就像在Java的String hashCode方法中一样。这是一个微妙的差异,当你的字符串是8个字符更长时,它会改变你的价值。