【问题标题】:Java Array HashCode implementationJava Array HashCode 实现
【发布时间】:2010-10-19 04:29:16
【问题描述】:

这很奇怪。一位同事询问了 java 中 myArray.hashCode() 的实现。我以为我知道,但后来我进行了一些测试。检查下面的代码。我注意到的奇怪的事情是,当我写出第一个系统时,结果是不同的。请注意,它几乎就像是在报告内存地址并修改类移动地址或其他东西。只是想我会分享。

int[] foo = new int[100000];
java.util.Random rand = new java.util.Random();

for(int a = 0; a < foo.length; a++) foo[a] = rand.nextInt();

int[] bar = new int[100000];
int[] baz = new int[100000];
int[] bax = new int[100000];
for(int a = 0; a < foo.length; a++) bar[a] = baz[a] = bax[a] = foo[a];

System.out.println(foo.hashCode() + " ----- " + bar.hashCode() + " ----- " + baz.hashCode() +  " ----- " + bax.hashCode());

// returns 4097744 ----- 328041 ----- 2083945 ----- 2438296
// Consistently unless you modify the class.  Very weird
// Before adding the comments below it returned this:
// 4177328 ----- 4097744 ----- 328041 ----- 2083945


System.out.println("Equal ?? " +
  (java.util.Arrays.equals(foo, bar) && java.util.Arrays.equals(bar, baz) &&
  java.util.Arrays.equals(baz, bax) && java.util.Arrays.equals(foo, bax)));

【问题讨论】:

    标签: java integer hashcode


    【解决方案1】:

    java.lang.ArrayhashCode方法继承自Object,这意味着hashcode依赖于引用。要根据数组的内容获取哈希码,请使用Arrays.hashCode

    请注意它是一个浅哈希码实现。还存在一个深度实现Arrays.deepHashCode

    【讨论】:

    • 感谢您的回答,但为什么 java.lang.Array 默认不覆盖 hashCode(和 toString)方法?有什么好的理由吗?
    • 因为 hashCode 需要快速才能有用(因为它主要用于防止昂贵的 .equals 调用),即使是数组上的浅值 hashCode 也可能非常慢。基本上是随机的 hashCode 并没有什么坏处,它只是没有提供任何优势。两害相权取其轻。
    • @Torque 只有当 equals() 以同样的方式蹩脚时才不会受到伤害。通常,“基本上随机”的 hashCode 将是一个严重的问题,因为如果 equals 为真,则 hashCode 必须相同。常数比随机更好。
    • 我很久以前写的评论,所以我不能说我当时的想法,但我可能指的是系统hashcode,它会返回一个不是基于对象字段(因此“基本上是随机的”),但对于调用之间的对象当然保持不变。我显然措辞不佳。
    【解决方案2】:

    数组使用基于内存位置的默认哈希码(但它不一定是 内存位置,因为它只是一个 int 并且所有内存地址都不适合) .您还可以通过打印System.identityHashCode(foo) 的结果来看到这一点。

    只有equal 的数组是相同的,相同的数组。所以,数组哈希码只有在它们是相同的、相同的数组的情况下才会相等。

    【讨论】:

    • (对象在内存中移动,如果您查看哈希码,它们通常看起来不像地址)
    • 对于 最近 版本的 Java,默认的 JVM 行为甚至不将身份哈希码 基于 内存地址。
    【解决方案3】:

    Object.hashCode() 的默认实现确实是返回对象的指针值,尽管这取决于实现。例如,一个 64 位 JVM 可以将指针和 XOR 以及高位和低位字放在一起。如果有意义,鼓励子类覆盖此行为。

    但是,对可变数组执行相等比较是没有意义的。如果一个元素发生变化,那么两者就不再相等。为了保持同一个数组无论其元素发生什么都将始终返回相同的 hashCode 的不变性,数组不会覆盖默认的 hashcode 行为。

    请注意,java.util.Arrays 提供了一个 deepHashCode() 实现,当基于数组内容而不是数组本身的标识进行散列很重要时。

    【讨论】:

    • 现代虚拟机在内存中移动对象。当前地址可以用作种子,但需要存储结果。
    • 在内存中移动仍然不会导致 hashCode 改变。
    【解决方案4】:

    我同意使用 java.util.Arrays.hashCode(或 google guava 通用包装器 Objects.hashcode),但请注意,如果您使用 Terracotta,这可能会导致问题 - 请参阅 this link

    【讨论】:

      猜你喜欢
      • 2019-06-02
      • 1970-01-01
      • 2012-04-04
      • 1970-01-01
      • 1970-01-01
      • 2013-10-13
      • 1970-01-01
      • 2012-04-16
      • 1970-01-01
      相关资源
      最近更新 更多