通用对象的哈希函数

时间:2012-09-26 08:20:42

标签: java data-structures hashmap hashtable

你如何为通用对象提出哈希函数?有两个对象需要具有相同哈希值的约束,如果它们是"相等"由用户定义。 Java如何实现这一目标?

6 个答案:

答案 0 :(得分:2)

首先,基本上你可以通过覆盖hashCode() method来定义类的哈希函数。 Javadoc说:

  

hashCode的一般合约是:

     
      
  • 每当在执行Java应用程序期间多次在同一对象上调用它时,hashCode方法必须始终返回相同的整数,前提是不修改对象的equals比较中使用的信息。从应用程序的一次执行到同一应用程序的另一次执行,此整数不需要保持一致。
  •   
  • 如果两个对象根据equals(Object)方法相等,则对两个对象中的每个对象调用hashCode方法必须生成相同的整数结果。
  •   
  • 如果两个对象根据equals(java.lang.Object)方法不相等,则不需要在两个对象中的每一个上调用hashCode方法必须生成不同的整数结果。但是,程序员应该知道为不等对象生成不同的整数结果可能会提高哈希表的性能。
  •   

所以更重要的问题是:是什么让你的两个物体相等?反之亦然:哪些属性使您的对象与众不同?如果您有答案,请创建一个比较所有属性的equals()方法,如果它们全部相同则返回true,否则返回false

hashCode()方法更复杂一些,我建议您不要自己创建它,但让IDE执行它。在Eclipse中,您可以从菜单中选择 Source 然后生成hashCode()和equals()。这也保证了上述要求。


这是一个小的(简化的)示例,其中使用Eclipse生成了两个方法。请注意,我选择不包含city属性,因为zipCode已经唯一标识了某个国家/地区内的城市。

public class Address {

    private String streetAndNumber;
    private String zipCode;
    private String city;
    private String country;

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((country == null) ? 0 : country.hashCode());
        result = prime * result
                + ((streetAndNumber == null) ? 0 : streetAndNumber.hashCode());
        result = prime * result + ((zipCode == null) ? 0 : zipCode.hashCode());
        return result;
    }

    @Override
    public boolean equals(final Object obj) {
        if(this == obj)
            return true;
        if(obj == null)
            return false;
        if(!(obj instanceof Address))
            return false;
        final Address other = (Address) obj;
        if(country == null) {
            if(other.country != null)
                return false;
        }
        else if(!country.equals(other.country))
            return false;
        if(streetAndNumber == null) {
            if(other.streetAndNumber != null)
                return false;
        }
        else if(!streetAndNumber.equals(other.streetAndNumber))
            return false;
        if(zipCode == null) {
            if(other.zipCode != null)
                return false;
        }
        else if(!zipCode.equals(other.zipCode))
            return false;
        return true;
    }
}

答案 1 :(得分:2)

我刚刚找到了自己问题的答案。 Java的工作方式是为每个对象定义一个hashCode,默认情况下,如果两个对象在内存中相同,则两个对象的hashCode是相同的。因此,当哈希表的客户端覆盖对象的equals()方法时,他还应该重写计算哈希码的方法,这样如果a.equals(b)为真,那么a.hashCode()也必须等于b.hashCode ()。这样,可以确保相等的对象具有相同的哈希码。

答案 2 :(得分:2)

Java不这样做。如果未显式实现hashCode()和equals(),则JVM将为有意义的相等实例生成不同的hashCodes。您可以通过Joshua Bloch查看Effective Java。这真的很有帮助。

答案 3 :(得分:0)

有几个选择:

  • 阅读Joshua Bloch的Effective Java。它包含一个很好的哈希码算法
  • 让您的IDE生成hashCode方法
  • Java SE 7及更高版本:使用Objects.hash

答案 4 :(得分:0)

班级java.lang.Object作弊。它将相等性(由equals确定)定义为对象标识(可以由==确定)。因此,除非你在子类中重写equals,否则你的类的两个实例是“相等的”,如果它们碰巧是同一个对象。

相关的哈希代码由系统函数System.identityHashCode实现(它不再真正基于对象地址 - 它曾经是吗? - 但可以被认为是以这种方式实现的)。

如果您覆盖equals,则hashCode的此实现不再有意义。

考虑以下示例:

class Identifier {

    private final int lower;
    private final int upper;

    public boolean equals(Object any) {
        if (any == this) return true;
        else if (!(any instanceof Identifier)) return false;
        else {
            final Identifier id = (Identifier)any;
            return lower == id.lower && upper == id.upper;
        }
    }
}

如果它们的“lower”和“upper”成员具有相同的值,则认为此类的两个实例是相等的。由于现在由对象成员确定相等性,我们需要以兼容的方式定义hashCode

public int hashCode() {
    return lower * 31 + upper;  // possible implementation, maybe not too sophisticated though
}

如您所见,我们使用hashCode中的相同字段,我们在确定相等性时也使用这些字段。将散列码基于所有成员通常是个好主意,在比较相等时也会考虑这些。

请考虑这个例子:

class EmailAddress {

    private final String mailbox;
    private final String displayName;

    public boolean equals(Object any) {
        if (any == this) return true;
        else if (!(any instanceof EmailAddress)) return false;
        else {
            final EmailAddress id = (EmailAddress)any;
            return mailbox.equals(id.mailbox);
        }
    }
}

从这里开始,相等只由mailbox成员决定,哈希码也应仅基于该成员:

public int hashCode() {
    return mailbox.hashCode();
}

答案 5 :(得分:-2)

通过覆盖hashCode() method来建立对象的哈希值,开发人员可以覆盖它。

Java在默认的哈希码计算中使用素数。

如果未实现equals()hashCode()方法,JVM将为对象隐式生成哈希码(对于Serializable类,生成serialVersionUID