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


当前回答

如果我正确理解你的问题,你有一个自定义的集合类(即一个从集合接口扩展的新类),你想实现hashCode()方法。

如果您的集合类扩展了AbstractList,那么您就不必担心它,因为已经有equals()和hashCode()的实现,它通过遍历所有对象并将它们的hashCodes()相加来工作。

   public int hashCode() {
      int hashCode = 1;
      Iterator i = iterator();
      while (i.hasNext()) {
        Object obj = i.next();
        hashCode = 31*hashCode + (obj==null ? 0 : obj.hashCode());
      }
  return hashCode;
   }

现在,如果你想要的是计算特定类哈希码的最佳方法,我通常使用^(按位排他或)操作符来处理我在equals方法中使用的所有字段:

public int hashCode(){
   return intMember ^ (stringField != null ? stringField.hashCode() : 0);
}

其他回答

由于您特别要求集合,我想添加一个其他答案还没有提到的方面:HashMap不期望它们的键在添加到集合后改变它们的hashcode。会破坏整个目的…

如果我正确理解你的问题,你有一个自定义的集合类(即一个从集合接口扩展的新类),你想实现hashCode()方法。

如果您的集合类扩展了AbstractList,那么您就不必担心它,因为已经有equals()和hashCode()的实现,它通过遍历所有对象并将它们的hashCodes()相加来工作。

   public int hashCode() {
      int hashCode = 1;
      Iterator i = iterator();
      while (i.hasNext()) {
        Object obj = i.next();
        hashCode = 31*hashCode + (obj==null ? 0 : obj.hashCode());
      }
  return hashCode;
   }

现在,如果你想要的是计算特定类哈希码的最佳方法,我通常使用^(按位排他或)操作符来处理我在equals方法中使用的所有字段:

public int hashCode(){
   return intMember ^ (stringField != null ? stringField.hashCode() : 0);
}

如果你对dmeister推荐的Effective Java实现感到满意,你可以使用一个库调用来代替自己的调用:

@Override
public int hashCode() {
    return Objects.hash(this.firstName, this.lastName);
}

这需要Guava (com.google.common.base.Objects.hashCode)或Java 7中的标准库(Java .util. objects .hash),但工作方式相同。

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

虽然这链接到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));
}