我们如何决定集合的hashCode()方法的最佳实现(假设equals方法已被正确重写)?


当前回答

我在arrays . deephashcode(…)周围使用了一个小包装器,因为它可以正确地处理作为参数提供的数组

public static int hash(final Object... objects) {
    return Arrays.deepHashCode(objects);
}

其他回答

虽然这链接到Android文档(Wayback Machine)和我自己在Github上的代码,但它一般适用于Java。我的答案是dmeister的答案的扩展,只是代码更容易阅读和理解。

@Override 
public int hashCode() {

    // Start with a non-zero constant. Prime is preferred
    int result = 17;

    // Include a hash for each field.

    // Primatives

    result = 31 * result + (booleanField ? 1 : 0);                   // 1 bit   » 32-bit

    result = 31 * result + byteField;                                // 8 bits  » 32-bit 
    result = 31 * result + charField;                                // 16 bits » 32-bit
    result = 31 * result + shortField;                               // 16 bits » 32-bit
    result = 31 * result + intField;                                 // 32 bits » 32-bit

    result = 31 * result + (int)(longField ^ (longField >>> 32));    // 64 bits » 32-bit

    result = 31 * result + Float.floatToIntBits(floatField);         // 32 bits » 32-bit

    long doubleFieldBits = Double.doubleToLongBits(doubleField);     // 64 bits (double) » 64-bit (long) » 32-bit (int)
    result = 31 * result + (int)(doubleFieldBits ^ (doubleFieldBits >>> 32));

    // Objects

    result = 31 * result + Arrays.hashCode(arrayField);              // var bits » 32-bit

    result = 31 * result + referenceField.hashCode();                // var bits » 32-bit (non-nullable)   
    result = 31 * result +                                           // var bits » 32-bit (nullable)   
        (nullableReferenceField == null
            ? 0
            : nullableReferenceField.hashCode());

    return result;

}

EDIT

通常,当重写hashcode(…)时,还需要重写equals(…)。所以对于那些将要或已经实现等于的人,这里有一个来自我的Github的很好的参考…

@Override
public boolean equals(Object o) {

    // Optimization (not required).
    if (this == o) {
        return true;
    }

    // Return false if the other object has the wrong type, interface, or is null.
    if (!(o instanceof MyType)) {
        return false;
    }

    MyType lhs = (MyType) o; // lhs means "left hand side"

            // Primitive fields
    return     booleanField == lhs.booleanField
            && byteField    == lhs.byteField
            && charField    == lhs.charField
            && shortField   == lhs.shortField
            && intField     == lhs.intField
            && longField    == lhs.longField
            && floatField   == lhs.floatField
            && doubleField  == lhs.doubleField

            // Arrays

            && Arrays.equals(arrayField, lhs.arrayField)

            // Objects

            && referenceField.equals(lhs.referenceField)
            && (nullableReferenceField == null
                        ? lhs.nullableReferenceField == null
                        : nullableReferenceField.equals(lhs.nullableReferenceField));
}

任何在可能的范围内均匀分布哈希值的哈希方法都是一个很好的实现。参见effective java (http://books.google.com.au/books?id=ZZOiqZQIbRMC&dq=effective+java&pg=PP1&ots=UZMZ2siN25&sig=kR0n73DHJOn-D77qGj0wOxAxiZw&hl=en&sa=X&oi=book_result&resnum=1&ct=result),其中有一个关于hashcode实现的好技巧(第9项我认为…)

当组合哈希值时,我通常使用boost c++库中使用的组合方法,即:

seed ^= hasher(v) + 0x9e3779b9 + (seed<<6) + (seed>>2);

这在确保平均分配方面做得相当好。有关这个公式如何工作的一些讨论,请参阅StackOverflow的帖子:boost::hash_combine中的魔术数字

在http://burtleburtle.net/bob/hash/doobs.html上有关于不同哈希函数的很好的讨论

对于简单类,通常最容易基于equals()实现检查的类字段实现hashCode()。

public class Zam {
    private String foo;
    private String bar;
    private String somethingElse;

    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }

        if (obj == null) {
            return false;
        }

        if (getClass() != obj.getClass()) {
            return false;
        }

        Zam otherObj = (Zam)obj;

        if ((getFoo() == null && otherObj.getFoo() == null) || (getFoo() != null && getFoo().equals(otherObj.getFoo()))) {
            if ((getBar() == null && otherObj. getBar() == null) || (getBar() != null && getBar().equals(otherObj. getBar()))) {
                return true;
            }
        }

        return false;
    }

    public int hashCode() {
        return (getFoo() + getBar()).hashCode();
    }

    public String getFoo() {
        return foo;
    }

    public String getBar() {
        return bar;
    }
}

最重要的是保持hashCode()和equals()的一致性:如果equals()对于两个对象返回true,那么hashCode()应该返回相同的值。如果equals()返回false,那么hashCode()应该返回不同的值。

最好使用Eclipse提供的功能,它做得非常好,您可以把精力和精力放在开发业务逻辑上。