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

问题描述:

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

+2

退房System.identityHashCode()它给你默认的哈希码 - 这将如果你没有覆盖的方法已经返回了一个。 – 2011-12-14 15:58:02

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

1

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

+16

我强烈反对“转换内部地址”,我一直想知道Sun/Oracle是否会从javadoc中删除该行。对象的内部地址无法保证在JVM中保持不变,其堆垃圾收集器可能会在堆压缩期间移动它。 – 2011-12-14 15:13:04

+0

这是否意味着他们一直都是独一无二的? – ernesto 2015-05-22 12:44:56

+15

JavaDoc引用是正确的,但答案不是。对象的地址多年未使用。 – 2015-09-08 07:01:47

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

有关information阅读此文章。

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

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

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

这与equals的默认实现一致。如果你想覆盖你的对象的equals,你将不得不调整hashCode,使它们一致。

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

hashCode()实现可以从类别不同类但hashCode()合同是非常具体的,并在Javadocs清楚明确地指出:

返回该对象的哈希码值。这种方法支持散列表的好处,例如java.util.Hashtable提供的散列表。

hashCode的一般合同是:

  • 每当它是一个Java应用程序的执行期间,在同一对象不止一次上调用,hashCode方法必须一致地返回相同的整数,没有提供所使用的信息在等于比较对象被修改。该整数不需要从应用程序的一次执行到同一应用程序的另一次执行保持一致。
  • 如果两个对象根据equals(Object)方法相等,则对这两个对象中的每个对象调用hashCode方法必须产生相同的整数结果。
  • 根据equals(java.lang.Object)方法,如果两个对象不相等,则不要求对两个对象中的每个对象调用hashCode方法必须产生不同的整数结果。但是,程序员应该意识到,为不相等的对象生成不同的整数结果可能会提高哈希表的性能。

尽管合理实用,类Object定义的hashCode方法确实为不同的对象返回不同的整数。 (这一般是通过将该对象的内部地址转换成一个整数来实现的,但不是由的JavaTM编程语言不需要这种实现技巧。)

hashCode()是紧密联系在一起的equals(),如果你重写equals(),你应该也覆盖hashCode()

+1

“的hashCode ()与equals()紧密相关,如果你实现了一个,你应该实现另一个“并不完全正确。如果重写equals,则只需要重写hashCode。在不重写equals的情况下重写hashCode在技术上是有效的。 – 2010-02-11 03:09:05

+0

@Steve Kuo:够公平的。根据您的评论,我重新措辞了最后一句。 – Asaph 2010-02-11 04:54:39

两个具有不同的散列码的对象必须不相对于equals()方法等于

a.hashCode() != b.hashCode()必须隐含!a.equals(b)

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

如果哈希码不重写,你将调用对象的hashCode,这里是从它的javadoc的摘录:

提供如合理可行得多,由Object类定义的hashCode方法不会返回针对不同的对象不同的整数。 (这一般是通过将该对象的内部地址转换成一个整数来实现的,但不是由的JavaTM编程语言不需要这种实现技巧。)

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

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

+0

OpenJDK的http://hg.openjdk.java.net/jdk6/jdk6-gate/jdk/file/tip/src/share/classes/java/lang/Object.java有它的原生功能。我希望看到本地函数的实现... http://*.com/questions/410756/is-it-possible-to-browse-the-source-of-openjdk-online。任何人? – 2011-12-14 15:54:55

+0

也许默认的hashcode方法存储第一个值并且从不改变它。 – 2011-12-14 18:28:56

返回6位十六进制数。这通常是对象所在的插槽的内存位置。从每本身的算法,我想JDK有双重散列(机实现),这是开放解决的最佳散列函数之一。这种双重哈希方案极大地降低了碰撞的可能性。

下面的文章将给予支持的想法 -

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

你必须在每一个覆盖等于类重载hashCode。如果不这样做将导致违规对象的总承包。的hashCode,这将防止你的类从正常在conjunction with all hash-based collection S,including HashMap, HashSet, and Hashtable.

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

  • 0:使用全局随机生成器。这是Java 7中的默认设置。它的缺点是来自多个线程的并发调用可能会导致争用条件,这将导致为不同对象生成相同的hashCode。而且在高度并发环境中,由于争用(使用来自不同CPU内核的相同内存区域),可能会出现延迟。
  • 5:使用一些线程局部xor-shift随机生成器,它不存在以前的缺点。这是Java 8.默认设置
  • 1:这是对“停止 - 的世界”的事件发生变化,所以停止了世界的事件之间(如垃圾收集)一些随机值混合使用对象的指针产生的散列码是稳定的(用于测试/调试的目的)
  • 2:始终使用1(用于测试/调试的目的)
  • 3:使用自动递增号码(用于测试/调试的目的,还使用全局计数器,从而争用和竞争条件是可能的)
  • 4:如果必要的话(用于测试/调试的目的)
使用对象指针修剪至32位

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