内存地址为HashTable中的哈希值。为什么HasTable不能使用密钥的实际内存地址作为哈希?

时间:2012-11-06 18:59:58

标签: java hash hashtable bucket

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

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

我看到一些帖子说密钥(对象)的默认hashCode()实际上是对象的内存地址,我认为这是不正确的。

我在一篇帖子中读到,说桶地址实际上是hash % number of existing buckets?这也不正确。

有人可以澄清吗?

6 个答案:

答案 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()对于同一个对象只相等。