1

可能这是一个基本问题或这背后的基本想法。

为什么 HasTable 不能使用 Key 的实际内存地址作为 Hash?或者哈希密钥的地址并使用它?

我看到一些帖子说hashCode()key(object)的默认值实际上是object的Memory地址,我认为这是不正确的。

我在一篇文章中读到,存储桶地址实际上是hash % number of existing buckets?这也不对。

有人可以澄清吗?

4

6 回答 6

8

如果一个类不覆盖hashCode(),而只是从 继承默认实现java.lang.Object,那么在典型的 JVM 中,它的hashCode(),实际上或多或少是指向它的内部指针。(显然这不是全部,因为hashCode()is的返回类型int不适合 64 位 JVM;而且这些不是指向物理内存位置的真正指针,首先因为操作系统处理从虚拟地址到物理地址的映射其次,因为即使 JVM 处理了这个问题,垃圾收集器也可以将一个对象从一个堆移动到另一个堆,而不会影响它的hashCode(). 但是,“内部内存地址”仍然是一个很好的近似值。)

大多数 JDK 类覆盖的原因hashCode()是我们总是希望hashCode()equals();兼容。也就是说,如果a.equals(b),那么我们想要a.hashCode() == b.hashCode()。(当您考虑到您通常不希望 - 例如 - a仅仅因为键是两个不同的实例而Map<String, Object>具有两个不同的条目时,这是有道理的。通常您希望能够通过键入来查找条目,而不是需要手边有键的原始实例。如果两个键相等,那么我们通常希望它们被视为相等。)"abc"Stringmap.get("abc")

如果您真的希望地图中的指针相等,则可以使用java.util.IdentityHashMapclass

于 2012-11-06T19:10:41.470 回答
3

默认Object.hashCode()值并不是严格意义上的内存地址,但除非您拥有巨大的内存,否则它在 JVM 中的所有对象中确实是唯一的,因此您可以将其视为“逻辑”地址。

HashMap 的桶数是有限的,每个键确实根据其哈希码分配了一个桶。每个哈希码没有一个桶。因此,即使两个对象具有不同的哈希码,它们也可能最终位于同一个存储桶中。这就是为什么尽可能好地分布 hashCode 以避免此类冲突很重要的原因。

大多数情况下,不希望使用密钥的系统身份哈希码(即返回的哈希码Object.hashCode()),因为如果它们持有相同的信息,您希望两个密钥相等,而不是如果它们是相同的对象实例。例如,如果您根据学生的 SSN 将学生存储在地图中,然后从某个 Web 服务或数据库中获取该学生的 SSN,您将不会拥有相同的 STring 实例,但您希望能够使用您收到的 SSN 在地图中查找学生。

于 2012-11-06T19:09:53.380 回答
1

为什么HashTable不能使用Key的实际内存地址作为Hash?

因为关键平等很重要。当两个对象“相等”(one.equals(two)返回 true)时,哈希码也必须相等(one.hashCode() == two.hashCode())。

于 2012-11-06T19:08:39.763 回答
1

默认hashCode()不是内存地址,而是“身份哈希”。

内存地址可能会改变,但对于给定实例,标识是不变的。

于 2012-11-06T19:09:10.400 回答
0

您可以拥有任何您认为最佳的 hashCode 实现,而不会破坏 JavaSE api 中为 hashCode 和 equals 制定的规则。

在此处检查等于和哈希码规则:http: //docs.oracle.com/javase/6/docs/api/java/lang/Object.html

这很重要,因为 Collections 库和其他 api 严重依赖此属性来实现自己的行为。

于 2012-11-06T19:08:23.377 回答
0

内存地址不应用作对象的 hashCode(除非其 equals 方法仅执行身份比较)。原因明确写在它的JavaDoc中:

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

在 equals 方法仅执行身份比较的情况下,内存地址就足够了,因为 hashCode() 和 equals() 仅对于同一个对象是相等的。

于 2012-11-06T19:10:45.577 回答