有效的java项9:覆盖hashcode示例

时间:2014-07-21 05:53:18

标签: java equals hashcode effective-java

我正在阅读Effective Java Item 9并决定自己运行示例代码。但它的工作方式略有不同,具体取决于我如何插入一个我不明白内部究竟发生了什么的新对象。 PhoneNumber类看起来:

public class PhoneNumber {

private final short areaCode;
private final short prefix;
private final short lineNumber;

public PhoneNumber(int areaCode, int prefix, int lineNumber) {
    this.areaCode = (short)areaCode;
    this.prefix = (short) prefix;
    this.lineNumber = (short)lineNumber;
}

@Override public boolean equals(Object o) {
    if(o == this) return true;
    if(!(o instanceof PhoneNumber)) return false;
    PhoneNumber pn = (PhoneNumber)o;
    return pn.lineNumber == lineNumber && pn.prefix == prefix && pn.areaCode == areaCode;
}
}

然后根据这本书,当我尝试时,

    public static void main(String[] args) {

         HashMap<PhoneNumber, String> phoneBook = new HashMap<PhoneNumber, String>();
         phoneBook.put(new PhoneNumber(707,867,5309), "Jenny");
         System.out.println(phoneBook.get(new PhoneNumber(707,867,5309)));
    }

这打印“null”并在本书中进行了解释,因为HashMap具有一种优化,可以缓存与每个条目关联的哈希码,如果哈希码不匹配则不会检查对象是否相等。对于我,这说得通。但是当我这样做时:

    public static void main(String[] args) {

         PhoneNumber p1 = new PhoneNumber(707,867,5309);
         phoneBook.put(p1, "Jenny");
         System.out.println(phoneBook.get(new PhoneNumber(707,867,5309)));
    }

现在它返回“Jenny”。你能解释为什么它在第二种情况下没有失败吗?

4 个答案:

答案 0 :(得分:1)

经验丰富的行为可能取决于用于运行应用程序的 Java版本供应商,因为违反了Object.hashcode()的常规合同,结果取决于实现。

可能的解释(采用HashMap的一种可能实现方式):

其内部实现中的HashMap类根据其哈希码将对象(键)放在不同的桶中。查询元素或检查映射中是否包含密钥时,首先根据查询密钥的哈希码查找正确的存储桶。在桶内对象以顺序方式检查,并且在桶内只有equals()方法用于比较元素。

因此,如果你不覆盖Object.hashcode(),如果2个不同的对象产生可能会或可能不会确定相同存储桶的默认哈希码,那么它将是不确定的。如果它们“指向”同一个桶,如果equals()方法表明它们相同,您仍然可以找到密钥。如果它们“指向”2个不同的桶,即使equals()方法表明它们是相同的,你也找不到密钥。

必须覆盖

hashcode()才能与重写的equals()方法保持一致。只有在这种情况下才能保证HashMap的正确,预期和一致的工作。

阅读Object.hashcode()的javadoc,了解您不得违反的合同。重点是,如果equals()为其他对象返回true,则hashcode()方法必须为这两个对象返回相同的值。

答案 1 :(得分:0)

  

你能解释为什么它在第二种情况下没有失败吗?

简而言之,它不能保证失败。第二个示例中的两个对象可能最终具有相同的哈希码(纯粹是巧合,或者更可能是由于编译器优化或由于默认hashCode()在JVM中的工作原理)。这将导致您描述的行为。

对于它的价值,我无法用我的编译器/ JVM重现这种行为。

答案 2 :(得分:0)

在巧合的情况下,JVM能够为两个对象找到相同的hashCode。当我运行你的代码时,在我的JVM中它为这两种情况都给出了null。所以你的问题是因为JVM而不是代码。

最好在每次覆盖equils()方法时覆盖hashCode()。 我还没读过Effective Java,我读过Kathy Sierra的SCJP。因此,如果您需要更多详细信息,那么您可以阅读本书。这真好。

答案 3 :(得分:0)

您上次修改的代码无法编译,因为您尚未声明phoneBook

两种主要方法应该完全相同。有一个16的机会它将打印Jenny,因为新创建的HashMap的默认大小为16.详细地说,这意味着只会检查hashCode的低4位。如果它们相等,则使用相等的方法。