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

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

通常,hashCode()只是返回对象的地址在内存中,如果你不覆盖它。

从1 :

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

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

  • 0:使用全局随机生成器。 这是Java 7中的默认设置。它具有的缺点是来自多个线程的并发调用可能会导致争用条件,这将导致为不同的对象生成相同的hashCode。 在高并发环境中,由于争用(使用来自不同CPU内核的相同内存区域),也可能会造成延迟。
  • 5:使用一些线程局部xor-shift随机生成器,它不存在以前的缺点。 这是Java 8中的默认设置。
  • 1:使用对象指针混合一些随机值在“停止世界”事件中改变,所以在停止世界事件(如垃圾收集)生成的哈希码是稳定的(用于testing/debugging目的)
  • 2:始终使用1 (用于testing/debugging目的)
  • 3:使用自动增加的数字(为了testing/debugging目的,也使用全局计数器,因此争用和竞争条件是可能的)
  • 4:如果需要,使用修剪为32位的对象指针(用于testing/debugging目的)

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

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

如果hashcode没有被覆盖,你会调用Object的hashcode,下面是javadoc的摘录:

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

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

这与equals的默认实现一致。 如果你想覆盖你的对象的等于,你将不得不适应hashCode,以便他们是一致的。

请参阅http://www.ibm.com/developerworks/java/library/j-jtp05273.html以获得良好的概述。;

你应该尝试实现哈希代码,以便不同的对象会给出不同的结果。 我不认为有这样做的标准方式。

阅读这篇文章的一些信息 。

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

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

具有不同散列码的两个对象不能等于equals()

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

然而,关于equals()的两个对象可以具有相同的哈希码。 如果多个对象具有相同的散列码,那么将这些对象存储在一个集合或一个映射中将变得效率较低。

不是一个真正的答案,而是增加了我以前的评论

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

返回6位hex数字。 这通常是对象所在的插槽的内存位置。 从algorithm本身来说,我猜JDK确实是双重哈希(本机实现),这是开放寻址最好的哈希函数之一。 这种双重哈希scheme极大地降低了碰撞的可能性。

下面的文章会给出一个支持的想法 –

Java – HashMap混淆了碰撞处理和get()方法

您必须在覆盖equals的每个类中重写hashCode。 如果不这样做将导致违反Object.hashCode的一般合约,这会阻止您的类conjunction with all hash-based collectionincluding HashMap, HashSet, and Hashtable. conjunction with all hash-based collection正常运行including HashMap, HashSet, and Hashtable.