我正在阅读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”。你能解释为什么它在第二种情况下没有失败吗?
答案 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位。如果它们相等,则使用相等的方法。