2010-03-11 66 views
44

hashCode()方法在java中返回什么值?如何在Java中计算hashCode()

我读到它是一个对象的内存引用...当我打印new Integer(1)的散列值是1;对于String("a")是97.

我很困惑:它是ASCII还是什么类型的值?

回答

40

哈希码是一个整数值,它表示被调用的对象的状态。这就是为什么设置为1的Integer将返回“1”的哈希码,因为Integer's哈希码及其值是相同的东西。一个字符的哈希码等于它的ASCII字符码。如果您编写自定义类型,则需要负责创建一个能够最好地表示当前实例状态的好实现。

42

hashCode()返回的值不能保证是对象的内存地址。我不确定Object类中的实现,但请记住,大多数类将覆盖hashCode(),使得两个语义等价(但不是相同实例)的实例将哈希到相同的值。如果这些类可以在另一个数据结构(例如Set)中使用,这依赖于与equals一致的hashCode,这一点尤其重要。

没有hashCode()唯一标识一个对象的实例,不管是什么。如果你想要一个基于底层指针的hashcode(例如在Sun的实现中),使用System.identityHashCode() - 这将委托给默认的hashCode方法,而不管它是否被覆盖。

尽管如此,即使System.identityHashCode()可以为多个对象返回相同的散列。请参阅注释以获得解释,但这里是一个示例程序,它持续生成对象,直到它找到两个具有相同的System.identityHashCode()。当我运行它时,它很快找到两个匹配的平均值,在将大约86,000个Long包装器对象(以及该键的整数包装器)添加到地图后平均匹配。

public static void main(String[] args) { 
    Map<Integer,Long> map = new HashMap<>(); 
    Random generator = new Random(); 
    Collection<Integer> counts = new LinkedList<>(); 

    Long object = generator.nextLong(); 
    // We use the identityHashCode as the key into the map 
    // This makes it easier to check if any other objects 
    // have the same key. 
    int hash = System.identityHashCode(object); 
    while (!map.containsKey(hash)) { 
     map.put(hash, object); 
     object = generator.nextLong(); 
     hash = System.identityHashCode(object); 
    } 
    System.out.println("Identical maps for size: " + map.size()); 
    System.out.println("First object value: " + object); 
    System.out.println("Second object value: " + map.get(hash)); 
    System.out.println("First object identityHash: " + System.identityHashCode(object)); 
    System.out.println("Second object identityHash: " + System.identityHashCode(map.get(hash))); 
} 

输出示例:

Identical maps for size: 105822 
First object value: 7446391633043190962 
Second object value: -8143651927768852586 
First object identityHash: 2134400190 
Second object identityHash: 2134400190 
+2

这被改装成下跌有什么特别的原因吗?如果这里有什么不正确的话,我会喜欢一个更正,但是如果没有解释就会失望,这对讨论没有任何帮助。 – danben 2010-03-11 20:09:38

+6

几年前,Ted Neward在http://blogs.tedneward.com/2008/07/16/ObjecthashCode+Implementation.aspx中解释了OpenJDK如何实现Object.hashCode()。 OpenJDK从对象地址派生散列码,但缓存该值并将其返回给后续调用者,以防对象在内存中移动并改变其地址。 在简要回顾了最新的代码之后,我发现自从Neward写他的文章以来,实现似乎没有改变。 – 2010-04-27 17:35:57

+0

这似乎支持我的答案。 – danben 2010-04-27 20:27:09

4

hashCode()方法通常用于识别一个对象。我认为Object实现返回对象的指针(不是一个真正的指针,而是一个唯一的ID或类似的东西)。但大多数类都会覆盖该方法。类似String类。两个String对象有不一样的指针,但他们是平等的:

new String("a").hashCode() == new String("a").hashCode() 

我认为hashCode()最常见的用途是在HashtableHashSet等。

Java API Object hashCode()

编辑: (由于最近的downvote和基于我阅读的关于JVM参数的文章)

随着JVM参数-XX:hashCode您可以更改hashCode的计算方式(请参阅Java专家通讯的Issue 222)。

HashCode == 0:只是返回随机数字,与内存中找不到的 无关。据我所知,种子的全局读写对于有很多处理器的系统来说并不是最佳的。

HashCode == 1:计算哈希代码值,不确定它们在什么值 开始,但它似乎相当高。

HashCode == 2:始终返回完全相同的标识哈希码1. 这可用于测试依赖于对象标识的代码。 原因为什么JavaChampionTest在上面的示例 中返回Kirk的URL的原因是所有对象都返回相同的哈希码。

HashCode == 3:从零开始计算哈希码值。它 看起来不是线程安全的,因此多个线程可以生成具有相同哈希代码的对象 。

HashCode == 4:这似乎与创建对象的内存位置 有一些关系。

HashCode> = 5:这是Java 8的默认算法,每个线程种子有一个 。它使用Marsaglia的xor-shift方案来生成 伪随机数。

+0

如果我正确阅读代码,则在Java 8和9中,默认策略为5. – 2017-09-01 22:56:34

1

如果内存正常工作(检查JavaDoc是否为java.lang.Object),则取决于实现,Object.hashCode()将根据对象而变化(Sun JVM从该值派生值对该对象的引用)。请注意,如果您正在实现任何非平凡对象,并且想要将它们正确存储在HashMap或HashSet中,则必须重写hashCode()和equals()。 hashCode()可以做任何你喜欢的事情(这是完全合法的,但让它返回1是不理想的),但重要的是如果你的equals()方法返回true,那么hashCode()返回的值对于两个对象是相等的。

混乱和缺乏对hashCode()和equals()的理解是错误的重要来源。确保你完全熟悉Object.hashCode()和Object.equals()的JavaDocs,并且我保证花费的时间会为自己付出代价。

5

我看,这是一个对象的内存引用..

Object.hashCode()用于大约14年前返回一个内存地址。从此以后。

什么类型的值是

它是什么完全取决于你在说什么类,它是否已经覆盖`是Object.hashCode()。

+1

这不提供问题的答案。要批评或要求作者澄清,请在其帖子下方留言。 – 2015-05-24 13:59:43

+0

@chsdk垃圾。它驳斥了这个问题中的一个断言。 '不'是答案。注意我也没有要求任何人澄清。 – EJP 2016-08-04 12:18:25

+0

这是一个自动评论,在一个标志为“不是答案”之后,在你最后一次编辑前2小时之前,这个答案还不清楚,并且无法解释。 – 2016-08-04 15:05:00

19

如果你想知道它们是如何被破坏的,我建议你阅读源代码。如果您使用的是IDE,您可以只使用您感兴趣的方法,并查看方法的实现方式。如果你不能这样做,你可以谷歌的来源。

例如,Integer。hashCode()方法被实现为

public int hashCode() { 
     return value; 
    } 

和String.hashCode()

public int hashCode() { 
     int h = hash; 
     if (h == 0) { 
      int off = offset; 
      char val[] = value; 
      int len = count; 

      for (int i = 0; i < len; i++) { 
       h = 31*h + val[off++]; 
      } 
      hash = h; 
     } 
     return h; 
    } 
+0

我已经计划以完全相同的方式回答; +1 – incarnate 2010-03-13 07:05:56

+0

@Peter Lawrey以及我如何看到Object hashCode的实现 – 2013-10-07 09:43:15

+0

@naroji它在OpenJDK中。不幸的是,有多种策略,不清楚使用哪一种。 – 2013-10-07 13:39:14

-1
public static int murmur3_32(int paramInt1, char[] paramArrayOfChar, int paramInt2, int paramInt3) { 
/* 121 */  int i = paramInt1; 
/*  */  
/* 123 */  int j = paramInt2; 
/* 124 */  int k = paramInt3; 
/*  */  
/*  */  int m; 
/* 127 */  while (k >= 2) { 
/* 128 */  m = paramArrayOfChar[(j++)] & 0xFFFF | paramArrayOfChar[(j++)] << '\020'; 
/*  */  
/* 130 */  k -= 2; 
/*  */  
/* 132 */  m *= -862048943; 
/* 133 */  m = Integer.rotateLeft(m, 15); 
/* 134 */  m *= 461845907; 
/*  */  
/* 136 */  i ^= m; 
/* 137 */  i = Integer.rotateLeft(i, 13); 
/* 138 */  i = i * 5 + -430675100; 
/*  */  } 
/*  */  
/*  */ 
/*  */ 
/* 143 */  if (k > 0) { 
/* 144 */  m = paramArrayOfChar[j]; 
/*  */  
/* 146 */  m *= -862048943; 
/* 147 */  m = Integer.rotateLeft(m, 15); 
/* 148 */  m *= 461845907; 
/* 149 */  i ^= m; 
/*  */  } 
/*  */  
/*  */ 
/*  */ 
/* 154 */  i ^= paramInt3 * 2; 
/*  */  
/*  */ 
/* 157 */  i ^= i >>> 16; 
/* 158 */  i *= -2048144789; 
/* 159 */  i ^= i >>> 13; 
/* 160 */  i *= -1028477387; 
/* 161 */  i ^= i >>> 16; 
/*  */  
/* 163 */  return i; 
/*  */ } 

如果你真的很想学习然后再通过Hashing.class可用此代码;

这里第一个参数HASHING_SEED是基于下面的代码

{ 
    long nanos = System.nanoTime(); 
    long now = System.currentTimeMillis(); 
    int SEED_MATERIAL[] = { 
      System.identityHashCode(String.class), 
      System.identityHashCode(System.class), 
      (int) (nanos >>> 32), 
      (int) nanos, 
      (int) (now >>> 32), 
      (int) now, 
      (int) (System.nanoTime() >>> 2) 
    }; 

    // Use murmur3 to scramble the seeding material. 
    // Inline implementation to avoid loading classes 
    int h1 = 0; 

    // body 
    for (int k1 : SEED_MATERIAL) { 
     k1 *= 0xcc9e2d51; 
     k1 = (k1 << 15) | (k1 >>> 17); 
     k1 *= 0x1b873593; 

     h1 ^= k1; 
     h1 = (h1 << 13) | (h1 >>> 19); 
     h1 = h1 * 5 + 0xe6546b64; 
    } 

    // tail (always empty, as body is always 32-bit chunks) 

    // finalization 

    h1 ^= SEED_MATERIAL.length * 4; 

    // finalization mix force all bits of a hash block to avalanche 
    h1 ^= h1 >>> 16; 
    h1 *= 0x85ebca6b; 
    h1 ^= h1 >>> 13; 
    h1 *= 0xc2b2ae35; 
    h1 ^= h1 >>> 16; 

    HASHING_SEED = h1; 
} 

所述第二参数是字符串的字符数组计算,第三个是总是“0”和第四个是char数组长度。

而上述计算仅适用于String哈希码。

对于所有整数,其散列码将是其整数值。 对于字符(最多两个字母)它将是ASCII码。

+0

我不知道这段代码来自哪里,但它与Java的'String :: hashCode()'计算方式无关。或者其他任何标准的Java类型的哈希码...... AFAIK。 String :: hashCode()的* real *代码在这里:http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/8u40-b25/java/lang/String的.java#String.hashCode%28%29 – 2017-09-01 22:30:02