为什么如果实现hashCode方法,如果在数据类型的Dictionary中键也必须实现equals方法?

时间:2009-10-28 12:43:28

标签: dictionary

数据类型:字典键

有人可以告诉我同时实现它们(hashCode / equals)的重要性。因为我认为如果我们实现hashCode方法equals将比较hashCodes并给我们相等。

4 个答案:

答案 0 :(得分:5)

HashCode不保证唯一性。例如,HashCode在大多数语言中占用2 ^ 32个值。如果你有一个4个整数的类,你可以拥有多少个可能的独特状态/实例? (2 ^ 32)^ 4。这意味着即使你实现了一个完美的哈希码,你仍然会有2 ^(32 * 3)个冲突,其中一对不同的对象具有相同的哈希码。

因此,HashCode用作第一个“快速”比较,用于查找与您要查找的对象类似的对象。一旦你找到一组对象,就会检查每个对象的相等性,看看是否有一个正是你正在寻找的对象。

答案 1 :(得分:4)

仅仅因为哈希码相等并不意味着底层对象是相等的。可能的哈希码数量有限,因此必然存在冲突。您应该实现一个健壮的.Equals(),以便您可以实际测试相等性。

答案 2 :(得分:3)

问题在于,仅仅因为两个对象具有相同的哈希码并不意味着它们是相同的。

只有2 ^ 32个可能的哈希码(32位整数)。如果你考虑一下,你会发现可能的字符串数量要大得多。因此,并非每个字符串都有唯一的哈希码。

此外,许多类的GetHashCode方法实施得很差。

例如,这里是来自.Net源代码的Point.GetHashCode

public override int GetHashCode() { 
    return x ^ y; 
}

请注意,(2, 3)将具有与(3, 2)相同的哈希码,即使它们不相等。虽然are implementations没有表现出这种行为,但根据定义,它们仍然不是唯一的。

答案 3 :(得分:0)

恕我直言,实现hashcode和equals的原因是:

哈希表允许基于密钥快速访问元素。这是可能的,因为它的实施。

哈希表在内部使用存储桶来存储其值。将每个桶视为一个数组。 并且有一系列这样的桶。因此它变成了二维阵列。密钥的哈希码是一种机制,通过该机制,哈希表可以直接跳转到存储该值的存储桶的索引。

例如:

下面我编写了一个Class的代码,我将用它作为HashMap实例的键。

package com.aneesh.hashtable;  
import java.util.HashMap;  
public class Key {

private String key;

public Key(String key){
    this.key = key;
}


@Override
public int hashCode() {
    return key.hashCode();
}


@Override
public boolean equals(Object obj) {
    if (this == obj)
        return true;
    if (obj == null)
        return false;
    if (getClass() != obj.getClass())
        return false;
    Key other = (Key) obj;
    if (key == null) {
        if (other.key != null)
            return false;
    } else if (!key.equals(other.key))
        return false;
    return true;
}


public static void main(String[] args) {

    HashMap<Key, String> hashMap = new HashMap<Key, String>();
    hashMap.put(new Key("a"), "java");
    hashMap.put(new Key("k"), "Python");

    System.out.println(hashMap.get(new Key("a")));
    System.out.println(hashMap.get(new Key("k")));

}
}

Key类的hashCode实现只是返回String类型的实例变量'key'的hashCode。 “a”的哈希码= 97 “k”= 107 //的哈希码有一个原因让我选择这两个键很快就会显而易见。

当你做hashMap.put(new Key(“a”),“java”); 哈希表必须确定将密钥值放入哪个存储桶。该代码将是

int indexofBucket = key.hashCode() % numberOfBuckets //7, where key is "a"

因此,(“a”,“java”)的键值对将被存储为第7个桶中的第一个元素。

当你做hashMap.put(new Key(“k”),“python”); 桶索引再次计算为     indexofBucket = key.hashCode()%numberOfBuckets // 7,其中key =“k”

这是同一个桶,第7个索引处的桶。

现在,当您通过键

检索值时
hashMap.get(new Key("a"));

哈希表会因此找出索引:

indexOfBucket = key.hashCode() % numberOfBuckets //7

此时哈希表会在桶中找到两个元素。现在哪个元素必须返回它将由(在一个简单的实现中我猜测)迭代每个元素并比较键的等于。如果没有等于,哈希表甚至可能无法找到您添加到其中的元素。

要查看此操作,请注释掉Key类的equals实现并运行代码。你会看到

null  
null 

作为输出打印,而实现等于,您将看到

的输出
"java",  
"python"

长期受伤的解释,但希望有所帮助