我知道只要在Java中覆盖equals
方法,就需要覆盖哈希码。那只是一份合同。我试图理解这背后的逻辑。我正在阅读Joshua Bloch的有效Java,我遇到了这段代码(第9项,第45页):
import java.util.HashMap;
import java.util.Map;
public final class PhoneNumber {
private final short areaCode;
private final short prefix;
private final short lineNumber;
public PhoneNumber(int areaCode, int prefix, int lineNumber) {
rangeCheck(areaCode, 999, "area code");
rangeCheck(prefix, 999, "prefix");
rangeCheck(lineNumber, 9999, "line number");
this.areaCode = (short) areaCode;
this.prefix = (short) prefix;
this.lineNumber = (short) lineNumber;
}
private static void rangeCheck(int arg, int max, String name) {
if (arg < 0 || arg > max)
throw new IllegalArgumentException(name + ": " + arg);
}
@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;
}
// Broken - no hashCode method!
// A decent hashCode method - Page 48
// @Override public int hashCode() {
// int result = 17;
// result = 31 * result + areaCode;
// result = 31 * result + prefix;
// result = 31 * result + lineNumber;
// return result;
// }
// Lazily initialized, cached hashCode - Page 49
// private volatile int hashCode; // (See Item 71)
//
// @Override public int hashCode() {
// int result = hashCode;
// if (result == 0) {
// result = 17;
// result = 31 * result + areaCode;
// result = 31 * result + prefix;
// result = 31 * result + lineNumber;
// hashCode = result;
// }
// return result;
// }
public static void main(String[] args) {
Map<PhoneNumber, String> m = new HashMap<PhoneNumber, String>();
m.put(new PhoneNumber(707, 867, 5309), "Jenny");
System.out.println(m.get(new PhoneNumber(707, 867, 5309)));
}
}
这是他在文中提到的,我很难理解。
此时,您可能希望
m.get(new PhoneNumber(707, 867, 5309))
返回“Jenny”,但它返回null。请注意两个 涉及PhoneNumber实例:一个用于插入 HashMap和第二个相等的实例用于(尝试) 恢复。 PhoneNumber类无法覆盖hashCode导致 这两个相等的实例具有不相等的哈希码,违反了 哈希码契约。因此get方法很可能会寻找 电话号码与其中的电话号码不同 由put方法存储
我不明白他谈到的两个PhoneNumber实例是什么。我只在m.put(new PhoneNumber(707, 867, 5309), "Jenny")
中创建了一个实例。此外,我再次查找此对象,即使它从Object Class继承hashCode方法,也应该返回相同的哈希码。
为什么会这样?这里的一些解释会有很多帮助。
答案 0 :(得分:4)
我不明白他谈到的两个PhoneNumber实例是什么。
第一个是你用来插入的那个。
m.put(new PhoneNumber(707, 867, 5309), "Jenny"));
第二个实例是用于检索的实例。
m.get(new PhoneNumber(707, 867, 5309)); // Notice the use of "new"
此外,我再次查找此对象,即使它从Object Class继承hashCode方法,也应返回相同的哈希码。
这是不正确的。 hashCode()
类中的Object
的默认实现为不同的对象返回不同的整数,因为它是通过将对象的内部地址转换为整数来实现的。因此,哈希码检查在那里失败。
另一方面,如果您尝试使用相同的实例检索PhoneNumber
PhoneNumber phoneNum = new PhoneNumber(707, 867, 5309);
m.put(phoneNum, "Jenny"));
m.get(phoneNum); // Not NULL
哈希码检查将通过(因为,您使用了之前插入的相同对象)和equals()
。这当然不是推荐的方法,因为我们更有可能使用与用于插入的关键对象不同的关键对象。
然而,使用的密钥将是有意义的等价物(如不同的String对象,但其文本相同),因此需要提供hashCode()
实现才能使匹配和检索正确发生。
答案 1 :(得分:3)
如果您没有覆盖hashcode
以及equals
,那么每个实例,例如“new PhoneNumber(707, 867, 5309)"
将使用不同的哈希码。
因此,从HashMap的角度来看,它们将被视为两个不同的条目。 只需阅读有关hashmap如何工作的更多信息。因此,如果两个可能相同但具有不同hascode的对象将存储在不同的存储桶中。
答案 2 :(得分:0)
转到此link
哈希码用于维护契约并唯一标识hashmap或hashtable中的每个对象。
答案 3 :(得分:0)
对于你的问题。
此外,我再次查找此对象,即使它从Object Class继承hashCode方法,也应返回相同的哈希码。
检查Object#hashCode文档here
尽可能合理,
the hashCode method defined by class Object does return distinct integers for distinct objects
。 (这通常通过将对象的内部地址转换为整数来实现,但JavaTM编程语言不需要此实现技术。)
换句话说,除非你覆盖它,否则hashCode
不会为两个对象返回相同的整数。
答案 4 :(得分:0)
您认为您只有一个实例,但您有两个实例。每个
new PhoneNumber(707,867,5309)
在另一个内存位置创建一个实例。哈希映射方法 m.get 正在查找您在方法调用中创建的新实例。此实例具有 m.put 方法中第一个创建的实例的另一个哈希码。
(因为有两个对象实例,Java将在超类Object中计算不同的hashCode。)
因此哈希映射找不到第二个对象的hashCode的第一个对象。
请将电话号码存储在一个变量中,并使用该变量进行放置,以便它可以正常工作 - 因为它是同一个内存位置的相同对象,具有相同的hashCode和equals == true。 / p>
public static void main(String[] args) {
Map<PhoneNumber, String> m = new HashMap<PhoneNumber, String>();
PhoneNumber num = new PhoneNumber(707, 867, 5309);
m.put(num, "Jenny");
System.out.println(m.get(num));
}
但是对于实际使用情况,您必须正确实施hashCode
和equals
方法。
答案 5 :(得分:0)
hashCode()
的目的是快速识别物体不相等的东西;对hashCode()
的一次调用将立即显示一个对象不等于已调用hashCode()
方法且返回不同值的任何对象。这是一个非常强大的功能,但它要求任何两个“相等”的对象必须返回相同的hashCode
值。如果两个对象没有返回相同的hashCode
值,则某些集合类型将假定它们可能不相等,并且不会打扰调用equals
以查看它们是否可能。