【发布时间】:2023-03-17 21:34:02
【问题描述】:
如果 hashCode() 方法没有被覆盖,那么在 Java 中对任何对象调用 hashCode() 的结果是什么?
【问题讨论】:
-
查看 System.identityHashCode() 它会为您提供默认哈希码 - 如果您没有覆盖该方法,则会返回该哈希码。
-
这取决于您使用的 JVM。热点见this
如果 hashCode() 方法没有被覆盖,那么在 Java 中对任何对象调用 hashCode() 的结果是什么?
【问题讨论】:
默认的 hashCode() 实现与对象的内存地址无关。 在 openJDK 版本 6 和 7 中,它是一个随机生成的数字。在 8 和 9 中,它是一个基于线程状态的数字。
参考此链接:hashCode != address
所以身份哈希生成的结果(hashCode()方法的默认实现返回的值)生成一次,并缓存在对象的头部中。
如果你想了解更多关于这方面的信息,你可以通过 OpenJDK 来定义 hashCode() 的入口点
src/share/vm/prims/jvm.h
和
src/share/vm/prims/jvm.cpp
如果您浏览上面的这个目录,似乎数百行函数似乎要复杂得多。因此,为了简化这一点,表示默认哈希码实现的天真方式如下所示,
if (obj.hash() == 0) { obj.set_hash(generate_new_hash()); } return obj.hash();
【讨论】:
hashCode() 的实现可能因类而异,但hashCode() 的合同非常具体,并在Javadocs 中明确明确地说明:
返回对象的哈希码值。支持这种方法是为了哈希表,例如 java.util.Hashtable 提供的哈希表。
hashCode的通用合约是:
- 只要在 Java 应用程序执行期间对同一个对象多次调用,hashCode 方法必须始终返回相同的整数,前提是没有修改对象上的 equals 比较中使用的信息。该整数不需要从应用程序的一次执行到同一应用程序的另一次执行保持一致。
- 如果根据 equals(Object) 方法,两个对象相等,则对两个对象中的每一个调用 hashCode 方法必须产生相同的整数结果。
- 如果根据 equals(java.lang.Object) 方法,如果两个对象不相等,则不要求对两个对象中的每一个调用 hashCode 方法必须产生不同的整数结果。但是,程序员应该意识到,为不相等的对象生成不同的整数结果可能会提高哈希表的性能。
在合理可行的情况下,由 Object 类定义的 hashCode 方法确实为不同的对象返回不同的整数。 (这通常通过将对象的内部地址转换为整数来实现,但 JavaTM 编程语言不需要这种实现技术。)
hashCode() 与 equals() 密切相关,如果你覆盖 equals(),你也应该覆盖 hashCode()。
【讨论】:
在 HotSpot JVM 中,默认情况下,在第一次调用非重载 Object.hashCode 或 System.identityHashCode 时会生成一个随机数并将其存储在对象头中。随后对Object.hashCode 或System.identityHashCode 的调用只是从标头中提取此值。默认情况下,它与对象内容或对象位置没有任何共同之处,只是随机数。此行为由-XX:hashCode=n HotSpot JVM 选项控制,该选项具有以下可能值:
1(用于测试/调试目的)请注意,即使您设置了-XX:hashCode=4,hashCode 也不会总是指向对象地址。对象可以稍后移动,但 hashCode 将保持不变。此外,对象地址分布不均(如果您的应用程序使用的内存不多,大多数对象将彼此靠近),因此如果使用此选项,您最终可能会得到不平衡的哈希表。
【讨论】:
-XX:hashCode=n是一个实验特性,需要先启用它然后使用上面的标志,所以组合的vm标志是:-XX:+UnlockExperimentalVMOptions -XX:hashCode=4,在java-11中测试。
您必须在每个覆盖 equals 的类中覆盖 hashCode。 否则将导致违反 Object.hashCode 的一般约定,这将阻止您的类在 @987654321 中正常运行@s, including HashMap, HashSet, and Hashtable.
【讨论】:
通常,如果您不覆盖它,hashCode() 只会返回对象在内存中的地址。
来自1:
在合理可行的情况下,由 Object 类定义的 hashCode 方法确实为不同的对象返回不同的整数。 (这通常通过将对象的内部地址转换为整数来实现,但 JavaTM 编程语言不需要这种实现技术。)
【讨论】:
hashCode“可能是也可能不是在某个时间点实现为对象内存地址的某个函数”,然后,the mentioning of addresses got removed completely,消除了任何混乱的根源。案件结案。
返回 6 位十六进制数。这通常是对象被寻址的槽的内存位置。从算法本身来看,我猜 JDK 做了双重哈希(本机实现),这是开放寻址的最佳哈希函数之一。这种双重哈希方案大大降低了冲突的可能性。
下面的帖子会给出一个支持性的想法-
Java - HashMap confusion about collision handling and the get() method
【讨论】:
不是真正的答案,而是添加到我之前的评论中
无法保证对象的内部地址在 JVM 中保持不变,其垃圾收集器可能会在堆压缩期间移动它。
我试图做这样的事情:
public static void main(String[] args) {
final Object object = new Object();
while (true) {
int hash = object.hashCode();
int x = 0;
Runtime r = Runtime.getRuntime();
List<Object> list = new LinkedList<Object>();
while (r.freeMemory() / (double) r.totalMemory() > 0.3) {
Object p = new Object();
list.add(p);
x += object.hashCode();//ensure optimizer or JIT won't remove this
}
System.out.println(x);
list.clear();
r.gc();
if (object.hashCode() != hash) {
System.out.println("Voila!");
break;
}
}
}
但是哈希码确实没有改变……谁能告诉我 Sun 的 JDK 是如何实现 Obect.hashcode 的?
【讨论】:
如果 hashcode 没有被覆盖,你将调用 Object 的 hashcode,这里是它的 javadoc 的摘录:
在合理的情况下,Object 类定义的 hashCode 方法确实为不同的对象返回不同的整数。 (这通常通过将对象的内部地址转换为整数来实现,但 JavaTM 编程语言不需要这种实现技术。)
【讨论】:
对于equals(),具有不同哈希码的两个对象不能相等
a.hashCode() != b.hashCode() 必须隐含!a.equals(b)
但是,对于 equals() 不相等的两个对象可以具有相同的哈希码。如果许多对象具有相同的哈希码,则将这些对象存储在集合或映射中的效率会降低。
【讨论】:
默认的哈希码实现给出了 jvm 中对象的内部地址,作为 32 位整数。因此,两个不同的(在内存中的)对象会有不同的哈希码。
这与equals的默认实现是一致的。如果你想为你的对象覆盖 equals,你必须调整 hashCode 以使它们保持一致。
请参阅http://www.ibm.com/developerworks/java/library/j-jtp05273.html 以获得良好的概述。
【讨论】:
哈希码对于将对象存储在集合中很有用,例如哈希集。通过允许 Object 将 Hashcode 定义为唯一的东西,它允许 HashSet 的算法有效地工作。
对象本身使用对象在内存中的地址,这是非常独特的,但如果两个不同的对象(例如两个相同的字符串)应该被认为是相同的,即使它们在内存中是重复的,也可能不是很有用。
【讨论】:
您应该尝试实现哈希码,以便不同的对象会给出不同的结果。我认为没有标准的方法可以做到这一点。
阅读这篇文章了解一些information。
【讨论】: