hashCode()是如何用Java计算的

Jot*_*thi 52 java hashcode

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

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

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

dan*_*ben 45

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

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

尽管如此,甚至System.identityHashCode()可以为多个对象返回相同的哈希值.请参阅注释以获得解释,但这是一个示例程序,它连续生成对象,直到找到两个相同的对象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)));
}
Run Code Online (Sandbox Code Playgroud)

示例输出:

Identical maps for size:  105822
First object value: 7446391633043190962
Second object value: -8143651927768852586
First object identityHash:  2134400190
Second object identityHash: 2134400190
Run Code Online (Sandbox Code Playgroud)

  • 是的:它可以"击中"另一个新对象的哈希码.Hashcodes不被认为是唯一的."hashcode"旨在"缩小"唯一性(对于Hashtables),但必须始终使用"equals". (8认同)
  • 几年前,Ted Neward在http://blogs.tedneward.com/2008/07/16/ObjecthashCode+Implementation.aspx中解释了OpenJDK如何实现Object.hashCode().OpenJDK从对象地址派生哈希代码,但缓存此值并将其返回给后续调用者,以防对象在内存中移动并且其地址发生更改.在简要回顾了最新的代码之后,我发现自从Neward撰写他的文章以来,实现似乎没有改变. (7认同)
  • 这有什么特别的原因被改变了吗?如果这里的某些内容不正确,我会喜欢纠正,但是在没有任何解释的情况下进行讨论就不会增加讨论内容. (2认同)

And*_*are 44

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


Pet*_*rey 22

如果你想知道它们是如何实现的,我建议你阅读源代码.如果您使用的是IDE,则只需对您感兴趣的方法执行+操作,并查看方法的实现方式.如果你不能这样做,你可以谷歌搜索来源.

例如,Integer.hashCode()实现为

   public int hashCode() {
       return value;
   }
Run Code Online (Sandbox Code Playgroud)

和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;
   }
Run Code Online (Sandbox Code Playgroud)


ale*_*ter 7

hashCode()方法通常用于识别对象.我认为Object实现返回对象的指针(不是真正的指针,而是唯一的id或类似的东西).但大多数类都会覆盖该方法.像String班级一样.两个String对象没有相同的指针,但它们是相同的:

new String("a").hashCode() == new String("a").hashCode()
Run Code Online (Sandbox Code Playgroud)

我认为最常见的用途hashCode()Hashtable,HashSet等.

Java API对象hashCode()

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

使用JVM参数,-XX:hashCode您可以更改hashCode的计算方式(请参阅Java专家通讯的第222期).

HashCode == 0:简单地返回随机数,与内存中找到对象的位置无关.据我所知,种子的全局读写对于拥有大量处理器的系统来说并不是最佳的.

HashCode == 1:计算哈希码值,不确定它们的起始值是多少,但它看起来很高.

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

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

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

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


use*_*421 5

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

Object.hashCode()用于返回大约14年前的内存地址的号码.不是以后.

什么类型的价值

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

  • 这不能为问题提供答案。要批评或要求作者澄清,请在其帖子下方发表评论。 (2认同)
  • @chsdk我无法辨别关于“曾经”和“从此以后不再”的任何“不清楚”或“无法解释”的内容。评论是如何到达这里的并不重要。 (2认同)

Jor*_*eld 5

来自 OpenJDK 来源 (JDK8):

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

product(intx, hashCode, 5,                                                
      "(Unstable) select hashCode generation algorithm")       
Run Code Online (Sandbox Code Playgroud)

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

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

然后,该函数创建 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;
}
Run Code Online (Sandbox Code Playgroud)

所以我们可以看到,至少在 JDK8 中默认设置为随机线程特定。