java 随机整数作为hashCode

gfttwv5a  于 2023-08-02  发布在  Java
关注(0)|答案(3)|浏览(123)

在构造函数中生成一个随机数并从hashCode方法返回这个值是个好主意吗?有可能发生冲突,但这适用于编写自己的hashCode方法。那么,有哪些缺点呢?当在HashMap中使用这个对象时,它将与随机数一起存储为散列,然后由相同的对象检索。如果存在冲突,则通过equals解决。

6l7fqoea

6l7fqoea1#

hashCode合约规定,除其他事项外,
如果根据equals(Object)方法,两个对象相等,则对这两个对象中的每一个调用hashCode方法都必须产生相同的整数结果。
所以,不,让它随机是一个坏主意。

hiz5n14c

hiz5n14c2#

基本答案

在典型的用例中,您希望检索的不是用于将其插入到散列集合中的相同(就标识而言)键对象,而是逻辑上等效(就相等而言)的对象。
因此,您希望下面的代码在Map中查找元素

Key k1 = new Key("param1", "param2");
Key k2 = new Key("param1", "param2");
Value val1 = new Value();

map.put(k1, value);

Value val2 = map.get(k2);

字符串
如果hashCode完全基于随机数,那么k1和k2具有不同的哈希值,因此可能会指向HashMap的不同桶。
我们证明了对于实现equals的对象,随机hashCode是一个糟糕的想法。这是哈希码合约部分背后的原因:
如果根据equals(Object)方法,两个对象相等,那么对这两个对象中的每一个调用hashCode方法必须产生相同的整数结果。

高级一点的部分:

Let's take a look at Object.hashCode implementation。请注意,身份hashCode()实现依赖于JVM。
查看JDK源代码,get_next_hash提供了六种计算hashCode的方法:

0. A randomly generated number.
1. A function of memory address of the object.
2. A hardcoded 1 (used for sensitivity testing.)
3. A sequence.
4. The memory address of the object, cast to int.
5. Thread state combined with xorshift (https://en.wikipedia.org/wiki/Xorshift)


参考代码:

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

OpenJDK 7和OpenJDK 6都使用第一种方法,即随机数生成器。

OpenJDK 8将默认状态更改为5线程状态并结合xorshift。
注意,Object.hashCode的所有这些实现都与Object.equals一致(就散列代码契约而言)
总而言之,您将实现Object.hashCode背后的策略之一,但是如果您实现equals,则会冒着破坏契约的风险。

yqkkidmi

yqkkidmi3#

如果你什么都不做,只使用Object.hashCode()(对象的“内存地址”),那么你或多或少已经得到了你想要的东西。因此,您可以拥有任何类的HashMap/HashSet键。
安全的equals

相关问题