【问题标题】:Why does Object.hashcode() have conflicts in Java?为什么 Object.hashcode() 在 Java 中有冲突?
【发布时间】:2012-02-22 15:44:05
【问题描述】:

我在 Windows XP 上的 Hotspot JDK 1.6 中运行以下代码, 我运行了两次,结果如下。

所以基本上看来object.hashcode() 也有冲突? 看起来它没有返回 VM 中的内存地址。

但是,JDK 中的注释说值应该是不同的,谁能解释一下?

在合理可行的情况下,hashCode 方法定义为 类 Object 确实为不同的返回不同的整数 对象。 (这通常是通过转换内部实现 对象的地址转换成整数,但是这个实现 技术是不需要的 JavaTM 编程语言。)

@return  a hash code value for this object.
@see     java.lang.Object#equals(java.lang.Object)
@see     java.util.Hashtable

这是第一个结果:

i,hashcode(): 361,9578500
i,hashcode(): 1886,9578500
conflict:1886, 361
i,hashcode(): 1905,14850080
i,hashcode(): 2185,14850080
conflict:2185, 1905
9998

这是第二个结果:

i,hashcode(): 361,5462872
i,hashcode(): 1886,29705835
conflict:1887, 362
i,hashcode(): 1905,9949222
i,hashcode(): 2185,2081190
conflict:2186, 1906
9998
10000

我的代码:

@Test
    public void testAddr()
    {
        Set<Integer> s = new TreeSet<Integer>();
        Map<Integer, Integer> m = new TreeMap<Integer, Integer>();
        Set<Object> os = new HashSet<Object>();

        for(int i = 0; i < 10000; ++i)
        {
            Object o = new Object();
            os.add(o);
            Integer h = o.hashCode();

            if((i == 361) || (i == 1886) || (i == 2185) || (i == 1905))
            {
                System.out.println("i,hashcode(): " + i + "," + h);
            }

            if(s.contains(h))
            {
                System.out.println("conflict:" + i + ", " + m.get(h));
            }
            else
            {
                s.add(h);   
                m.put(h,  i);
            }

        }


        System.out.println(s.size());

        int c = 0;
        for(Object o: os)
        {
            c++;
        }

        System.out.println(c);
    }

【问题讨论】:

  • 评论并没有说它将是不同的,而是他们会尝试使其与众不同但不提供任何保证
  • “在合理可行的范围内”,它会返回不同的代码。不保证。
  • 尽可能实用是理解为什么会出现哈希码冲突的关键短语。这不是错误。
  • 我的问题更多是:windows 的 Object.hashcode() 实现上的热点 jdk 是什么?它是返回对象的内存地址吗?如果是,那么这里应该是不同的,因为我在最后的代码下面,这意味着所有的对象还没有被 GC 收集;诠释 c = 0; for(对象 o: os) { c++; }

标签: java hashcode


【解决方案1】:

hashCode() 应该用于在hash tables 中放置对象。 hashCode 的规则是hashCode 永远不应产生冲突,尽管这是一个理想的属性,但相等的对象必须具有相等的哈希码。这并不排除不相等的对象具有相等的哈希码。

您发现默认Object.hashCode() 实现确实为不相等的对象生成相等的哈希码的情况。要求对象的哈希码不改变,除非该对象与另一个对象的某些字段影响相等性发生变化。一个可能的原因是垃圾收集器重新排列了内存,因此o 的后续实例与o 的早期实例位于同一位置(也就是说,您在循环中分配了两个对象o,而垃圾收集器在两次分配之间重新排列内存,以便将旧的o 移出内存的一个位置,然后在该位置分配新的o)。那么,即使旧o的哈希码不能改变,新o的哈希码就是新o在内存中的存储地址,恰好等于老o

【讨论】:

  • Integer 的 hashCode 是 Integer 的值。这意味着他为他的两个对象取回了相同的哈希码。我不确定你指的那条线是怎么出问题的。。
  • @JamesMontagne 你是对的。大多数类(正确地)没有指定hashCode() 的实现细节,但是像String 这样的一些核心Java 类会这样做。我必须检查文档以确保,但Integer.hashCode() 确实返回了整数的基础值。我已经编辑了我的答案,以使这一点更清楚。
  • 我的问题更多是:windows实现Object.hashcode()的热点jdk是什么?它是返回对象的内存地址吗?如果是,那么这里应该是不同的,因为我在最后的代码下面,这意味着所有的对象还没有被 GC 收集;诠释 c = 0; for(对象 o: os) { c++; }
  • @hetaoblog OpenJDK 实现默认返回地址的修改版本。显然,地址的低位可能为零,并且在 2 的幂的简单哈希表中会很糟糕(HashMap 因为大约 1.4.1/2 “重新哈希”哈希值以打乱位)。出于调试目的,有构建选项始终返回 1(最大冲突)或使用安全随机数生成器(速度慢,但冲突最小的基准)。我相信 IBM 正在使用一种实现,其中散列值应该是不可猜测的(显然它们不是)。
  • @hetaoblog 我不知道Windows上的HotSpot JVM是如何实现Object.hashCode()的,但是我在第二段中描述的情况与对象是否被垃圾回收无关。 JRE 完全可以随意重新排列内存,它可以这样做的一种方法是在代之间移动对象(请参阅oracle.com/technetwork/java/javase/…)。如果将旧的o 移到tenured 代,将新的o 分配到young generation,就会出现我描述的情况。
【解决方案2】:

不幸的是,这是对 API 文档的常见误解。来自前段时间的一个仍未修复(1 票)的错误。

(spec) System.identityHashCode doc inadequate, Object.hashCode default implementation docs mislead

[...]

从 Usenet 讨论和开源软件看来, 许多(也许是大多数)程序员认为这意味着 默认实现,因此 System.identityHashCode,将 产生唯一的哈希码。

建议的实施技术甚至不适合 现代无句柄 JVM,应该和 JVM 规范章节一样 9.

“尽可能合理实用”的限定条件是,在 实践,不足以明确哈希码不是,在 实践,与众不同。

【讨论】:

    【解决方案3】:

    一个长时间运行的程序可能会在运行期间创建、调用hashCode() 并放弃数十亿个对象。因此,在数学上不可能确保一旦某个对象hashCode 返回一个特定的数字,在程序的生命周期内没有其他对象会返回相同的数字。即使hashCode() 以某种方式设法为前 4,294,967,296 个对象返回唯一值,它也别无选择,只能为下一个对象返回一个已使用的值(因为上一个调用将使用最后一个剩余的以前未使用的值) .

    hashCode() 显然不能保证哈希值不会在程序的生命周期内被重用,但这并不意味着它不能保证哈希码不会在相关对象的生命周期内被重用.实际上,对于某些内存管理方案,可以相对便宜地做出这样的保证。例如,1984 年的 Macintosh 将堆分成两部分,一部分保存固定大小的对象描述符,另一部分保存可变大小的对象数据。对象描述符一旦创建就永远不会移动;如果删除了任何对象,则其描述符使用的空间将在创建新对象时重新使用。在这种方案下,只要对象存在,对象描述符的地址就代表其身份的唯一且不变的表示,因此可以用作hashCode() 值。不幸的是,与其他一些对象没有与之关联的固定地址的方法相比,这种方案往往具有更多的开销。

    【讨论】:

    • 我不知道的好观点!并感谢您提供一些有趣的背景知识!
    【解决方案4】:

    评论并没有说它是不同的。
    它说它是独特的尽可能实用

    显然,您发现了一个不实用的案例。

    【讨论】:

      【解决方案5】:

      哈希码不必唯一,只要保持一致即可。尽管它们通常是相当独特的。

      除了你上面的摘录之外,Object 还有以下要说的。

      在合理可行的情况下,由 Object 类定义的 hashCode 方法确实为不同的对象返回不同的整数。 (这通常通过将对象的内部地址转换为整数来实现,但 JavaTM 编程语言不需要这种实现技术。)

      Object Doc

      【讨论】:

        猜你喜欢
        • 2016-03-23
        • 2021-04-11
        • 1970-01-01
        • 2022-12-18
        • 2023-03-27
        • 2011-08-03
        • 2019-05-13
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多