-1

我正在阅读 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)));
    }

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

4

4 回答 4

1

所经历的行为可能取决于用于运行应用程序的Java 版本供应商,因为由于违反了Object.hashcode()的一般合同,因此结果取决于实现。

一种可能的解释(采取一种可能的实现HashMap):

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

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

hashcode()必须重写以与您的重写equals()方法一致。只有在这种情况下,才能保证HashMap.

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

于 2014-07-21T06:16:00.000 回答
0

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

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

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

于 2014-07-21T06:15:48.017 回答
0

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

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

于 2014-07-21T06:55:21.123 回答
0

您最后剪断的代码无法编译,因为您尚未声明phoneBook.

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

于 2014-07-21T15:02:07.210 回答