可能这是一个基本问题或这背后的基本想法。
为什么 HasTable 不能使用 Key 的实际内存地址作为 Hash?或者哈希密钥的地址并使用它?
我看到一些帖子说hashCode()
key(object)的默认值实际上是object的Memory地址,我认为这是不正确的。
我在一篇文章中读到,存储桶地址实际上是hash % number of existing buckets
?这也不对。
有人可以澄清吗?
如果一个类不覆盖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"
String
map.get("abc")
如果您真的希望地图中的指针相等,则可以使用java.util.IdentityHashMap
class。
默认Object.hashCode()
值并不是严格意义上的内存地址,但除非您拥有巨大的内存,否则它在 JVM 中的所有对象中确实是唯一的,因此您可以将其视为“逻辑”地址。
HashMap 的桶数是有限的,每个键确实根据其哈希码分配了一个桶。每个哈希码没有一个桶。因此,即使两个对象具有不同的哈希码,它们也可能最终位于同一个存储桶中。这就是为什么尽可能好地分布 hashCode 以避免此类冲突很重要的原因。
大多数情况下,不希望使用密钥的系统身份哈希码(即返回的哈希码Object.hashCode()
),因为如果它们持有相同的信息,您希望两个密钥相等,而不是如果它们是相同的对象实例。例如,如果您根据学生的 SSN 将学生存储在地图中,然后从某个 Web 服务或数据库中获取该学生的 SSN,您将不会拥有相同的 STring 实例,但您希望能够使用您收到的 SSN 在地图中查找学生。
为什么HashTable不能使用Key的实际内存地址作为Hash?
因为关键平等很重要。当两个对象“相等”(one.equals(two)
返回 true)时,哈希码也必须相等(one.hashCode() == two.hashCode()
)。
默认hashCode()
不是内存地址,而是“身份哈希”。
内存地址可能会改变,但对于给定实例,标识是不变的。
您可以拥有任何您认为最佳的 hashCode 实现,而不会破坏 JavaSE api 中为 hashCode 和 equals 制定的规则。
在此处检查等于和哈希码规则:http: //docs.oracle.com/javase/6/docs/api/java/lang/Object.html
这很重要,因为 Collections 库和其他 api 严重依赖此属性来实现自己的行为。
内存地址不应用作对象的 hashCode(除非其 equals 方法仅执行身份比较)。原因明确写在它的JavaDoc中:
如果两个对象根据 {@code equals(Object)} 方法相等,则对两个对象中的每一个调用 {@code hashCode} 方法必须产生相同的整数结果。
在 equals 方法仅执行身份比较的情况下,内存地址就足够了,因为 hashCode() 和 equals() 仅对于同一个对象是相等的。