【问题标题】:.Net equivalent of Java's System.identityHashCode().Net 等效于 Java 的 System.identityHashCode()
【发布时间】:2019-04-15 19:55:31
【问题描述】:

Java 的System.identityHashCode()

为给定对象返回与返回相同的哈希码 通过默认方法 hashCode(),无论给定对象的 类覆盖 hashCode()。

该哈希码基于对象标识,因此对于同一个对象,它始终是相同的,无论该对象在对identityHashCode() 的调用之间是否发生了变异。

除此之外,任何两个活对象之间都不会发生哈希冲突(使用一些 Java 运行时):(前者是 Oracle 在下面给出的源代码中的不准确陈述,正如 Jai 的答案显示,正如another bug report 所指出的那样 - 这基本上使我原来的问题无效......)

[...] 垃圾对象很容易回收并且地址空间是 重复使用。冲突是地址空间重用的结果。如果是原 对象保持活动状态(不是 GCed),那么您将不会遇到这个 问题。

Source

.Net中有RuntimeHelpers.GetHashCode(),满足第一个条件,但不满足第二个:

请注意,GetHashCode 总是为相等的对象引用返回相同的哈希码。然而,反之则不然:相等的哈希码并不表示相等的对象引用。特定的哈希码值对于特定的对象引用不是唯一的;不同的对象引用可以生成相同的哈希码。

那么 .Net 中是否有类似 Java 的 identityHashCode() 的东西?

编辑:

有人建议这与Memory address of an object in C# 相同,但它不是,因为内存地址不能在这里(单独)使用,因为内存管理会移动对象,因此地址可能会在对象的生命周期内发生变化.

【问题讨论】:

  • Java 不保证不同的对象会有不同的哈希码。
  • 保证与实施细节不同。
  • 为什么要删除我添加到您帖子中的I am trying to build a stable sort algorithm. 声明?它确实为您的问题提供了有用的背景。
  • 我还怀疑您误解了 Java 是如何做到这一点的。它确实保证了对象的哈希码不会改变(根据stackoverflow.com/questions/3796699/…)。但是如果发生 GC,内存被压缩并且对象被移动到别处,那么没有什么能阻止该类型的新实例占用与“原始”对象相同的内存地址。然后,您将拥有两个具有相同哈希码的对象。因此,您不能声明In addition to that, there will not be hash collisions between any two living objects
  • 虽然 Java 的 Object#hashCode()System.identifyHashCode() 保证它为特定 instance 返回的值永远不会改变,但它没有提到它将返回一个唯一的值每个对象。事实上,它只返回对象堆中的尾随 32 位内存地址——这意味着 0x 0 FFFF FFFF0x 1 FFFF FFFF 都将返回 0x FFFF FFFF。您可以修改错误报告的示例,例如创建一个静态列表来存储obj 以防止GC,仍然会发生冲突

标签: java c# .net hash


【解决方案1】:

目前 Java 的 Object#hashCode()System#identifyHashCode() 确实确保返回唯一值。对此已有疑问,this 就是一个例子。

您提到了一个错误报告,其中指出发生冲突因为对象被垃圾回收,并且相同的内存地址被重用。但是修改相同的测试用例会证明不是这样:

List<Object> allObjs = new ArrayList<>(); // Used to prevent GC
Set<Integer> hashes = new HashSet<Integer>(1024);

int colls = 0;
for (int n = 0; n < 100000; n++)
{
    Integer obj = new Integer(88);
    allObjs.add(obj); // keep a strong reference to prevent GC
    int ihash = System.identityHashCode(obj);
    Integer iho = Integer.valueOf(ihash);
    if (hashes.contains(iho))
    {
        System.err.println("System.identityHashCode() collision!");
        colls++;
    }
    else
    {
        hashes.add(iho);
    }
}

System.out.println("created 100000 different objects - "
        + colls
        + " times with the same value for System.identityHashCode()");

System.out.println("Size of all objects is " + allObjs.size());
System.out.println("Size of hashset of hash values is " + hashes.size());

结果:

System.identityHashCode() collision!
System.identityHashCode() collision!
System.identityHashCode() collision!
created 100000 different objects - 3 times with the same value for System.identityHashCode()
Size of all objects is 100000
Size of hashset of hash values is 99997

在链接的 SO 问题中,还提到在 JRE 的某些实现中,冲突率大大降低了。但是,似乎没有任何实现能够防止 all 冲突。因此,即使在 Java 中也无法确保哈希码的唯一性。

因此,不要只相信一个来源。评论它的人也只是 Oracle 团队的成员,他或她很可能不是设计这个的人。

在 C# 和 Java 中,您都必须创建自己的某种唯一数字生成器。所以 NPras 提供的 solution 似乎可以为 .NET 做到这一点。

【讨论】:

  • 你让我不再相信 Java 会防止冲突。考虑一下,非冲突实现也不会很快,因为它需要线程同步等。
  • @EugeneBeresovsky 为了防止冲突,JVM 必须阻止您在其运行时创建超过 2³² 的对象(这甚至假设每个对象都是 1 位)。听起来不是一个聪明的主意吗?如果哈希码实现返回long,那么它可能会非常安全…… 10 到 20 年?
【解决方案2】:

我会将您推荐给following answer from Eric Lippert(他是 C# 语言设计和编译器团队的成员),他建议使用 ObjectIDGenerator。

要为对象生成唯一 ID,您可以使用我们方便地为您提供的恰当命名的 ObjectIDGenerator

Looking at the reference source(好在他们现在开源了框架),它确实使用了RuntimeHelpers.GetHashCode(),但也通过单独存储引用来处理潜在的冲突。

请注意他关于对象生命周期的警告。如果您需要它用于瞬态对象,他建议您重新实现生成器 - 现在您可以更轻松地访问源代码。

【讨论】:

  • 我怀疑这是 OP 最接近答案的地方。但很难看出这对实现“稳定”排序有何用处。
  • 是的。但这可能应该是一个不同的问题,因为它已经被编辑了:) 此外,我看不出 java 的 identityHashCode 有什么帮助。
  • 确实如此。 :)
  • 感谢 NPras。然而,该 ObjectIDGenerator 使用强引用,因此它永远不会释放内存。使用带有弱引用的缓存 WeakReference 就可以了。
  • 是的,这就是他的建议。使用弱引用重新实现类。
猜你喜欢
  • 2011-03-23
  • 2012-11-28
  • 2010-11-20
  • 2011-01-27
  • 2010-09-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-04-29
相关资源
最近更新 更多