可能这是一个基本问题或背后的基本思想。
为什么HasTable不能将密钥的实际内存地址用作哈希?或者哈希密钥的地址并使用它?
我看到一些帖子说密钥(对象)的默认hashCode()
实际上是对象的内存地址,我认为这是不正确的。
我在一篇帖子中读到,说桶地址实际上是hash % number of existing buckets
?这也不正确。
有人可以澄清吗?
答案 0 :(得分:8)
如果一个类没有覆盖hashCode()
,并且只是继承了java.lang.Object
的默认实现,那么在一个典型的JVM中,它的hashCode()
实际上或多或少会是指向它的内部指针。 (显然这不是整个故事,因为hashCode()
的返回类型是int
,它不能容纳64位JVM;而这些不是物理内存位置的真正指针,首先是因为操作系统处理从虚拟地址到物理地址的映射,其次是因为,即使JVM处理了这一点,垃圾收集器也可以将对象从一个堆移动到另一个堆而不影响其hashCode()
。但仍然是“内部存储器”地址“是一个很好的第一近似值。”
大多数JDK类覆盖hashCode()
的原因是我们总是希望hashCode()
与equals()
“兼容”;也就是说,如果a.equals(b)
,那么我们需要a.hashCode() == b.hashCode()
。 (当你考虑到你通常不希望时 - 例如 - Map<String, Object>
有两个不同的"abc"
条目只是因为密钥是两个不同的String
个实例,这是有道理的。您希望能够通过键入map.get("abc")
来查找条目,而不是需要使用该键的原始实例。如果两个键相等,那么我们通常希望它们被视为相等。 )
如果您确实希望地图中指针相等,则可以使用the java.util.IdentityHashMap
class。
答案 1 :(得分:3)
默认的Object.hashCode()
并不是严格意义上的内存地址,但除非你拥有庞大的内存,否则它在JVM中的所有对象中确实是独一无二的,因此你可以将它视为“逻辑”地址。
HashMap具有有限数量的存储桶,并且每个密钥确实根据其哈希码分配了一个存储桶。每个哈希码没有一个桶。因此,即使两个对象具有不同的哈希码,它们也可能最终存在于同一个桶中。这就是为什么让hashCode尽可能分散是很重要的,以避免这种冲突。
在大多数情况下,使用密钥的系统标识哈希码(即Object.hashCode()
返回的哈希码)是不可取的,因为如果它们拥有相同的信息,则需要两个密钥相等,而不是它们是同一个对象实例。例如,如果您根据他的SSN将学生存储在地图中,然后从某个Web服务或数据库获取该学生的SSN,您将不会拥有相同的STring实例,但您希望能够使用您收到的SSN在地图中找到学生。
答案 2 :(得分:1)
为什么HashTable不能将密钥的实际内存地址用作哈希?
因为关键平等至关重要。当两个对象“相等”(one.equals(two)
返回true)时,哈希码也必须相等(one.hashCode() == two.hashCode()
)。
答案 3 :(得分:1)
默认hashCode()
不内存地址,它是“身份哈希”。
内存地址可能会更改,但对于给定的实例,标识是常量。
答案 4 :(得分:0)
你可以拥有任何你认为最佳的hashCode实现,而不会破坏为JavaC apis规定的hashCode和equals规则。
在此处检查等号和哈希码规则:http://docs.oracle.com/javase/6/docs/api/java/lang/Object.html
这很重要,因为Collections库和其他api严重依赖这些属性来实现自己的行为。
答案 5 :(得分:0)
内存地址不应该用作对象的hashCode(除非它的equals方法只执行身份比较)。原因明确写在JavaDoc:
中如果两个对象根据{@code equals(Object)}相等 方法,然后在两者中的每一个上调用{@code hashCode}方法 对象必须产生相同的整数结果。
在equals方法仅执行身份比较的情况下,内存地址就足够了,因为hashCode()和equals()对于同一个对象只相等。