hashCode()是如何用Java计算的

时间:2010-03-11 18:32:05

标签: java hashcode

hashCode()方法在java中返回什么值?

我读到它是对象的内存引用...当我打印new Integer(1)的哈希值时,它是1; String("a")为97.

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

10 个答案:

答案 0 :(得分:45)

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

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

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

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

答案 1 :(得分:44)

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

答案 2 :(得分:21)

如果您想知道它们的实施方式,我建议您阅读来源。如果您使用的是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;
   }

答案 3 :(得分:6)

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:只返回与where无关的随机数   在内存中找到了对象。就我所知,全球化   对于有很多种子的系统,种子的读写不是最佳的   处理器。

     

HashCode == 1:计算哈希码值,不确定值是多少   他们开始,但似乎很高。

     

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

     

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

     

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

     

HashCode&gt; = 5:这是Java 8的默认算法,并且有一个   每线程种子。它使用Marsaglia的xor-shift方案来制作   伪随机数。

答案 4 :(得分:5)

  

我读到它是对象的内存引用..

没有。 Object.hashCode()用于返回大约14年前的内存地址。不是从那以后。

  

是什么类型的价值

它究竟取决于你所说的是什么类,以及它是否覆盖了`Object.hashCode()。

答案 5 :(得分:2)

Object.hashCode(),如果内存正确服务(检查JavaDoc for java.lang.Object),是依赖于实现的,并将根据对象进行更改(Sun JVM从引用的值派生值)对象)。

请注意,如果要实现任何非平凡对象,并且想要将它们正确地存储在HashMap或HashSet中,则必须覆盖hashCode()和equals()。 hashCode()可以做任何你喜欢的事情(它完全是合法的,但是让它返回1是次优的。)但是,如果你的equals()方法返回true,那么hashCode()为两个对象返回的值是相等的,这一点至关重要。

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

答案 6 :(得分:2)

从OpenJDK来源(JDK8):

使用默认值5生成哈希码:

product(intx, hashCode, 5,                                                
      "(Unstable) select hashCode generation algorithm")       

一些恒定数据和一个随机生成的数字,每个线程启动一个种子:

// thread-specific hashCode stream generator state - Marsaglia shift-xor form
  _hashStateX = os::random() ;
  _hashStateY = 842502087 ;
  _hashStateZ = 0x8767 ;    // (int)(3579807591LL & 0xffff) ;
  _hashStateW = 273326509 ;

然后,此函数创建hashCode(如上所述,默认为5):

static inline intptr_t get_next_hash(Thread * Self, oop obj) {
  intptr_t value = 0 ;
  if (hashCode == 0) {
     // This form uses an unguarded global Park-Miller RNG,
     // so it's possible for two threads to race and generate the same RNG.
     // On MP system we'll have lots of RW access to a global, so the
     // mechanism induces lots of coherency traffic.
     value = os::random() ;
  } else
  if (hashCode == 1) {
     // This variation has the property of being stable (idempotent)
     // between STW operations.  This can be useful in some of the 1-0
     // synchronization schemes.
     intptr_t addrBits = cast_from_oop<intptr_t>(obj) >> 3 ;
     value = addrBits ^ (addrBits >> 5) ^ GVars.stwRandom ;
  } else
  if (hashCode == 2) {
     value = 1 ;            // for sensitivity testing
  } else
  if (hashCode == 3) {
     value = ++GVars.hcSequence ;
  } else
  if (hashCode == 4) {
     value = cast_from_oop<intptr_t>(obj) ;
  } else {
     // Marsaglia's xor-shift scheme with thread-specific state
     // This is probably the best overall implementation -- we'll
     // likely make this the default in future releases.
     unsigned t = Self->_hashStateX ;
     t ^= (t << 11) ;
     Self->_hashStateX = Self->_hashStateY ;
     Self->_hashStateY = Self->_hashStateZ ;
     Self->_hashStateZ = Self->_hashStateW ;
     unsigned v = Self->_hashStateW ;
     v = (v ^ (v >> 19)) ^ (t ^ (t >> 8)) ;
     Self->_hashStateW = v ;
     value = v ;
  }

  value &= markOopDesc::hash_mask;
  if (value == 0) value = 0xBAD ;
  assert (value != markOopDesc::no_hash, "invariant") ;
  TEVENT (hashCode: GENERATE) ;
  return value;
}

因此我们可以看到至少在JDK8中,默认设置是特定于随机线程的。

答案 7 :(得分:2)

定义:String hashCode() 方法以整数形式返回字符串的哈希码值。

语法: public int hashCode()

Hashcode 使用以下公式

计算
s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]

where:

s is ith character in the string
n is length of the string
^ is exponential operand

示例: 例如,如果您想计算字符串“abc”的哈希码,那么我们有以下详细信息

s[] = {'a', 'b', 'c'}
n = 3

因此哈希码值将计算为:

s[0]*31^(2) + s[1]*31^1 + s[2]
= a*31^2 + b*31^1 + c*31^0
= (ASCII value of a = 97, b = 98 and c = 99)
= 97*961 + 98*31 + 99 
= 93217 + 3038 + 99
= 96354

所以“abc”的哈希码值为96354

答案 8 :(得分:0)

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;
}

第二个参数是String的char数组,第三个是'0',第四个是char数组长度。

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

对于所有整数,其哈希码将是其整数值。 对于char(最多两个字母),它将是ASCII码。

答案 9 :(得分:0)

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

https://docs.oracle.com/javase/8/docs/api/java/lang/Object.html#hashCode--