如果没有覆盖hashCode(),那么对象的哈希码是什么?

时间:2010-02-10 15:02:42

标签: java object hashcode

如果没有覆盖hashCode()方法,那么在Java中的任何对象上调用hashCode()的结果是什么?

12 个答案:

答案 0 :(得分:53)

在HotSpot JVM中,默认情况下,在第一次调用非重载Object.hashCodeSystem.identityHashCode时,会生成一个随机数并存储在对象标头中。对Object.hashCodeSystem.identityHashCode的后续调用只是从标头中提取此值。默认情况下,它与对象内容或对象位置没有任何共同之处,只是随机数。此行为由-XX:hashCode=n HotSpot JVM选项控制,该选项具有以下可能的值:

  • 0:使用全局随机生成器。这是Java 7中的默认设置。它的缺点是来自多个线程的并发调用可能会导致竞争条件,这将导致为不同的对象生成相同的hashCode。此外,在高度并发的环境中,由于争用(使用来自不同CPU核心的相同内存区域),可能会出现延迟。
  • 5:使用一些没有先前缺点的线程局部xor-shift随机发生器。这是Java 8中的默认设置。
  • 1:使用对象指针与在“stop-the-world”事件上更改的一些随机值混合,因此在stop-the-world事件(如垃圾收集)之间生成的hashCodes是稳定的(用于测试/调试目的) )
  • 2:始终使用1(用于测试/调试目的)
  • 3:使用自动增量数字(用于测试/调试目的,也使用全局计数器,因此争用和竞争条件是可能的)
  • 4:如果需要,使用对象指针修剪为32位(用于测试/调试)

请注意,即使您设置了-XX:hashCode=4,hashCode也不会始终指向对象地址。可以稍后移动对象,但hashCode将保持不变。此外,对象地址的分布也很差(如果您的应用程序使用的内存不多,大多数对象将彼此靠近),因此如果使用此选项,最终可能会出现不平衡的哈希表。

答案 1 :(得分:32)

通常情况下,如果不覆盖对象的地址,hashCode()只返回对象的地址。

来自1

  

尽可能合理,Object类定义的hashCode方法确实为不同的对象返回不同的整数。 (这通常通过将对象的内部地址转换为整数来实现,但JavaTM编程语言不需要此实现技术。)

答案 2 :(得分:13)

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()

答案 3 :(得分:3)

如果hashcode没有被覆盖,你将调用Object的hashcode,这里是javadoc的摘录:

  

尽可能合理,Object类定义的hashCode方法确实为不同的对象返回不同的整数。 (这通常通过将对象的内部地址转换为整数来实现,但JavaTM编程语言不需要此实现技术。)

答案 4 :(得分:1)

默认的哈希码实现给出了jvm中对象的内部地址,为32位整数。因此,两个不同的(在内存中)对象将具有不同的哈希码。

这与equals的默认实现一致。如果要覆盖对象的equals,则必须调整hashCode以使它们保持一致。

有关概述,请参阅http://www.ibm.com/developerworks/java/library/j-jtp05273.html

答案 5 :(得分:1)

您必须在覆盖等于的每个类中覆盖hashCode。如果不这样做将导致违反Object.hashCode的常规合同,这将阻止您的类在{{{ 1}} s,conjunction with all hash-based collection

答案 6 :(得分:1)

默认的 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();

答案 7 :(得分:0)

您应该尝试实现哈希代码,以便不同的对象提供不同的结果。我不认为有这样做的标准方法。

阅读本文了解一些information

答案 8 :(得分:0)

哈希码对于将对象存储在集合中非常有用,例如哈希集。通过允许Object将Hashcode定义为唯一的东西,它允许HashSet的算法有效地工作。

对象本身在内存中使用Object的地址,这是非常独特的,但如果两个不同的对象(例如两个相同的字符串)应该被认为是相同的,即使它们在内存中重复,也可能不是很有用。

答案 9 :(得分:0)

对于equals()

,具有不同哈希码的两个对象不能相等

a.hashCode() != b.hashCode()必须暗示!a.equals(b)

但是,两个与equals()不相等的对象可以具有相同的哈希码。如果许多对象具有相同的哈希码,则将这些对象存储在集合或映射中的效率会降低。

答案 10 :(得分:0)

不是答案,而是添加到我之前的评论中

  

对象的内部地址不能保证在JVM中保持不变,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的吗?

答案 11 :(得分:0)

返回6位十六进制数。这通常是对象被寻址的槽的存储位置。从算法本身来看,我猜JDK会进行双重散列(本机实现),这是开放寻址的最佳散列函数之一。这种双重哈希方案大大降低了碰撞的可能性。

以下帖子将提供一个支持性的想法 -

Java - HashMap confusion about collision handling and the get() method