【问题标题】:Random integer as hashCode随机整数作为 hashCode
【发布时间】:2023-03-11 16:36:01
【问题描述】:

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

【问题讨论】:

  • 为什么要这样做?即与使用 Object 的默认实现相反,它具有正确的不变量

标签: java random hash hashcode


【解决方案1】:

hashCode contract 指定,除其他外,

如果两个对象根据equals(Object) 方法相等,那么对两个对象中的每一个调用hashCode 方法必须产生相同的整数结果。

所以不,让它随机是个坏主意。

【讨论】:

  • 如果未按规定遵守合同可能会出错:stackoverflow.com/a/62477121/6099347
  • 如果我们将“合同”抛在脑后并视之为不好的做法,那么实际意义是什么?您仍然可以正确实现equals,但是在使用HashMap 时,您最关心的是hashCode,对吗?如果碰撞较少,则地图将以优化的方式工作。
  • 坦率地说我不知道​​。但关键是您不应该关心(并且您应该关心合同,因为这些是其他代码将对您的代码做出的假设)。如果您确实在乎,那么您能做的最好的事情就是自己尝试一下,看看您是否获得了更好的性能,或者它是否打破了整个概念。
  • @Vallerious 不遵守合同会给系统带来错误,因为阅读您的代码的人并不总是有足够的时间浏览每一行。如果系统有 100 万行代码,那么没有人会阅读完整的代码,而是假设方法名称做了他们声称的事情,并且所有的合同都被遵守了。假设有人更改了具体的实现,例如,没有遍历具有 10k 行代码的整个类,在这种情况下,该人将引入错误。关键是,遵循合约总是更好,不用担心挤掉最后一个 CPU 周期。
  • @Vallerious 使用 HashMap 时,您关心equals 就像关心hashCode 一样多。检索对象的算法是获取所有具有相同哈希值的对象,然后对其进行迭代并返回相等的对象。
【解决方案2】:

如果你什么都不做并使用Object.hashCode()(对象的“内存地址”),那么你或多或少地拥有你想要实现的目标。因此,您可以拥有任何类的 HashMap/HashSet 键。

还有一个保险箱equals

【讨论】:

    【解决方案3】:

    基本答案

    在典型用例中,您不希望使用用于将其插入散列集合中的相同(就身份而言)键对象进行检索,而是使用逻辑上等价的对象(就相等而言) .

    因此,您希望以下代码在地图中查找元素

    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,则会冒着违反合同的风险。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-04-21
      • 2012-02-10
      • 2021-03-29
      • 2010-11-25
      • 2014-05-21
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多