澄清Java的HashSet / HashMap实现背后的事实

时间:2013-12-31 00:22:10

标签: java hash hashmap hashset

1。 我理解不同的哈希映射机制以及处理关键冲突的方式(开放寻址 - 线性/二次探测,链接,可扩展哈希等等.HashSet / HashMap使用哪一个?

2。 我意识到一个好的HashMap依赖于一个好的哈希函数。 Java的HashSet / HashMap如何散列对象?我知道有一个哈希函数,但到目前为止我还没有实现这个字符串。如果我现在想要哈希我创建的Java对象怎么办?我需要实现哈希函数吗?或者Java是否有内置的创建哈希码的方法?

我知道不能依赖默认实现,因为它将哈希函数基于不是常量的内存地址。

2 个答案:

答案 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的HashMapHashSet及相关类在hashcode()equals(Object)上提出以下要求:

  1. 如果a.equals(b)a.hashCode() == b.hashCode()
  2. 虽然a位于HashSet中,或者是HashMap中的密钥,但a.hashCode()返回的值不得更改。
  3. 如果!a.equals(b),那么a.hashCode() == b.hashCode()应该低的概率,尤其是ab可能是应用程序的哈希键。
  4. (出于性能原因的最后一个要求。如果你有一个“差”哈希函数导致不同的密钥散列相同的哈希码的概率很高,你会得到很多冲突。哈希链会变得不平衡,你不会获得通常预期的哈希表操作的平均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和hashCodeequals实现为return this == otherhashcode(实现所有意图和目的)实现为为每个实例分配随机标识符并将其用作hashCode

与Java extends Object中的所有类一样,它们都继承了这些实现。

默认情况下,某些类会覆盖这些实现。正如您所提到的,String是一个非常好的例子。另一个是集合API中的类 - 所以ArrayList根据它包含的元素实现这些方法。

就实施好hashCode而言,这是一种暗示艺术。 Here's a pretty good summary最佳做法。

您的最终评论:

我知道不能依赖默认实现,因为它将哈希函数基于不是常量的内存地址。

这是正确。 hashCode的默认实现是常量,因为这是方法合同的一部分。来自the Javadoc

  

每当在同一个对象上多次调用它时   执行Java应用程序时,hashCode方法必须始终如一   返回相同的整数,前提是equals中没有使用的信息   对象的比较被修改。不需要保留该整数   从一个应用程序的执行到另一个执行的一致性   相同的申请。