1。 我理解不同的哈希映射机制以及处理关键冲突的方式(开放寻址 - 线性/二次探测,链接,可扩展哈希等等.HashSet / HashMap使用哪一个?
2。 我意识到一个好的HashMap依赖于一个好的哈希函数。 Java的HashSet / HashMap如何散列对象?我知道有一个哈希函数,但到目前为止我还没有实现这个字符串。如果我现在想要哈希我创建的Java对象怎么办?我需要实现哈希函数吗?或者Java是否有内置的创建哈希码的方法?
我知道不能依赖默认实现,因为它将哈希函数基于不是常量的内存地址。
答案 0 :(得分:3)
您可以通过阅读the source code for HashMap
来自己回答其中的许多问题。
(提示:您通常可以使用Google查找Java SE类的源代码;例如搜索“java.util.HashMap source
”。)
我理解不同的哈希映射机制以及处理关键冲突的方式(开放寻址 - 线性/二次探测,链接,可扩展哈希等等.HashSet / HashMap使用哪一种?
链接。查看源代码。 (我链接的版本中的第154行)。
Java的HashSet / HashMap如何散列对象?
没有。调用对象的hashCode
方法来执行此操作。查看源代码。 (第360行)。
如果你看一下代码,你会看到一些有趣的皱纹:
代码(在我链接的版本中)是使用特殊方法散列字符串。 (看来这是为了允许在平台级别“调整”字符串的散列。我没有深入研究这个......)
Object.hashCode()
调用返回的哈希码进一步“加扰”以减少冲突的可能性。 (阅读评论!)
如果我现在想要哈希我创建的Java对象怎么办?我需要实现哈希函数吗?
你可以做到这一点。
您是否需要执行此操作取决于您为该课程定义equals
的方式。具体来说,Java的HashMap
,HashSet
及相关类在hashcode()
和equals(Object)
上提出以下要求:
a.equals(b)
则a.hashCode() == b.hashCode()
。a
位于HashSet
中,或者是HashMap
中的密钥,但a.hashCode()
返回的值不得更改。!a.equals(b)
,那么a.hashCode() == b.hashCode()
应该低的概率,尤其是a
和b
可能是应用程序的哈希键。(出于性能原因的最后一个要求。如果你有一个“差”哈希函数导致不同的密钥散列相同的哈希码的概率很高,你会得到很多冲突。哈希链会变得不平衡,你不会获得通常预期的哈希表操作的平均O(1)
性能。在最坏的情况下,性能将是O(N)
;即相当于链表的线性搜索。)
或者Java是否有内置的创建哈希码的方法?
每个类都从hashCode()
继承一个默认的Object
方法(除非被覆盖)。它使用所谓的“身份哈希码”;即基于对象标识(其引用)的哈希值。这与equals(Object)
的默认实现相匹配......它只使用==
来比较引用。
我知道不能依赖默认实现,因为它将哈希函数基于不是常量的内存地址。
这是不正确的。
默认的hashCode()
方法返回“身份哈希码”。这通常是基于对象的内存地址,但它不是对象的内存地址。
特别是,如果垃圾收集器移动了一个对象,则保证其“身份哈希码”不会改变。是。这是对的,它不会改变......即使物体被移动了!
(他们如何有效地实现这一点非常聪明。有关详细信息,请参阅https://stackoverflow.com/a/3796963/139985。)
底线是默认的Object.hashCode()
方法满足上面列出的所有要求。 可以依赖。
答案 1 :(得分:2)
问题1)
Java HashMap
实现使用链接实现来处理冲突。可以把它想象成一系列链表。
问题2
Object
的默认实现为equals和hashCode
。 equals
实现为return this == other
,hashcode
(实现所有意图和目的)实现为为每个实例分配随机标识符并将其用作hashCode
。
与Java extends Object
中的所有类一样,它们都继承了这些实现。
默认情况下,某些类会覆盖这些实现。正如您所提到的,String
是一个非常好的例子。另一个是集合API中的类 - 所以ArrayList
根据它包含的元素实现这些方法。
就实施好hashCode
而言,这是一种暗示艺术。 Here's a pretty good summary最佳做法。
您的最终评论:
我知道不能依赖默认实现,因为它将哈希函数基于不是常量的内存地址。
这是不正确。 hashCode
的默认实现是常量,因为这是方法合同的一部分。来自the Javadoc:
每当在同一个对象上多次调用它时 执行Java应用程序时,hashCode方法必须始终如一 返回相同的整数,前提是equals中没有使用的信息 对象的比较被修改。不需要保留该整数 从一个应用程序的执行到另一个执行的一致性 相同的申请。