为什么Object.hashcode()在Java中存在冲突?

时间:2012-02-22 15:44:05

标签: java hashcode

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

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

然而,JDK中的评论说这些值应该是不同的,任何人都可以解释一下吗?

  

尽可能合理实用,由hashCode方法定义   class Object确实返回distinct的不同整数   对象。 (这通常通过转换内部来实现   将对象的地址转换为整数,但是这个实现   技术不是必需的   Java TM 编程语言。)

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

5 个答案:

答案 0 :(得分:5)

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

您发现了一种情况,默认Object.hashCode()实现确实为不相等的对象生成相同的哈希码。除非该对象与另一个对象的某些字段感情相等,否则要求对象的哈希码不会改变。一个可能的原因是垃圾收集器重新安排了内存,以便稍后实例化oo的早期实例化位于同一位置(即,您分配了两个对象o循环和垃圾收集器在两个分配之间重新安排内存,以便将旧的o移出一个内存位置,然后在该位置分配新的o。然后,即使旧o的哈希码无法更改,新o的哈希码也是新o存储在内存中的地址,恰好相同到旧o的哈希码。

答案 1 :(得分:2)

遗憾的是,这是对API文档的常见误解。从一段时间以来仍未修复(1票)的错误。

  

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

     

[...]

     

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

     

建议的实现技术甚至不适合   现代无柄JVM,应该与JVM Spec Chapter一样   9。

     

资格“尽可能合理”,是   练习,不足以说明哈希码不是,在   练习,不同。

答案 2 :(得分:1)

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

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

答案 3 :(得分:0)

评论并未说它是截然不同的 它表示它与非常相似

显然,你发现了一个不切实际的案例。

答案 4 :(得分:0)

Hashcodes不必是唯一的,只是一致的。虽然它们通常是相当独特的。

除了上面的摘录,Object还有以下内容。

  

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

Object Doc