【发布时间】:2023-03-11 16:36:01
【问题描述】:
在构造函数中生成一个随机数并从hashCode方法返回这个值是个好主意吗?
有可能发生冲突,但这适用于编写自己的 hashCode 方法时。那么有什么缺点呢?
在 HashMap 中使用此对象时,它将与随机数一起存储为哈希,然后由其检索。如果有冲突,他们将由equals 解决。
【问题讨论】:
-
为什么要这样做?即与使用
Object的默认实现相反,它具有正确的不变量
在构造函数中生成一个随机数并从hashCode方法返回这个值是个好主意吗?
有可能发生冲突,但这适用于编写自己的 hashCode 方法时。那么有什么缺点呢?
在 HashMap 中使用此对象时,它将与随机数一起存储为哈希,然后由其检索。如果有冲突,他们将由equals 解决。
【问题讨论】:
Object 的默认实现相反,它具有正确的不变量
hashCode contract 指定,除其他外,
如果两个对象根据
equals(Object)方法相等,那么对两个对象中的每一个调用hashCode方法必须产生相同的整数结果。
所以不,让它随机是个坏主意。
【讨论】:
equals,但是在使用HashMap 时,您最关心的是hashCode,对吗?如果碰撞较少,则地图将以优化的方式工作。
equals 就像关心hashCode 一样多。检索对象的算法是获取所有具有相同哈希值的对象,然后对其进行迭代并返回相等的对象。
如果你什么都不做并使用Object.hashCode()(对象的“内存地址”),那么你或多或少地拥有你想要实现的目标。因此,您可以拥有任何类的 HashMap/HashSet 键。
还有一个保险箱equals。
【讨论】:
基本答案
在典型用例中,您不希望使用用于将其插入散列集合中的相同(就身份而言)键对象进行检索,而是使用逻辑上等价的对象(就相等而言) .
因此,您希望以下代码在地图中查找元素
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 是一个糟糕的主意。
这就是hash code contract部分背后的原因:
如果两个对象根据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)
OpenJDK 7 和 OpenJDK 6 都使用第一种方法,随机数生成器。
OpenJDK 8 将默认值更改为 5 - 线程状态与 xorshift 结合。
请注意,Object.hashCode 的所有这些实现都与 Object.equals 一致(在哈希码合约方面)
总而言之,您将实现 Object.hashCode 背后的策略之一,但如果您实现 equals,则会冒着违反合同的风险。
【讨论】: