What is a hash table?

In this article we will learn the most useful data structures - one of the hash table in English, the hash table is called Hash Table, also known as a hash table or Hash Table .

Using a hash table array of support in accordance with the characteristics of the index random access to data , so the hash table is actually an extension of the array, the array evolved. It can be said, without strings, there is no hash table.

Hash table is stored in a key data (key) and the value (value) thereof. For example, we will each gender as the data is stored, the key is a name, a value corresponding to the sex, gender M where M represents, F represents the female sex.

Why do we need a hash table?

In order to compare and hash table, we first data is stored in an array in.

Here prepared six boxes (ie, an array of length 6) to store data, assuming that we need to check the gender of Ally, Ally because they do not know which data is stored in the box, we can only scratch the query, this operation would perhaps for linear search . In general, we can put an identifier as a key data, as the value of the content data.

Find the box from No. 0, No. 0 found the key is stored in a box instead of Joe Ally, therefore then look for No. 1 box.

Oh excluded, No. 1 in the box is not Ally, no way, only then looking down.

A little worse, No. 2, No. 3 box is also not Ally.

Hard work pays off, when we find 4 boxes, I found where the key data for Ally, the value corresponding to the key out, we know that gender is female Ally (F).

Through the above search process, we found that the higher the amount of data, the longer time it takes to find linear. It can be seen: Because the query data is more time-consuming, so here is not suitable for use arrays to store data.

但使用哈希表便可以解决这个问题,首先准备好数组,这次我们用 5 个箱子的数组来存储数据。

尝试把 Joe 存进去,使用哈希函数(Hash)计算 Joe 的键,也就是字符串 Joe 的哈希值,比如得到的结果为4928。

将得到的哈希值除以数组的长度 5,求得其余数,这样的求余运算叫作mod运算,此处mod运算的结果为3。

因此,我们将 Joe 的数据存进数组的 3 号箱子中,重复前面的操作,将其他数据也存进数组中。

Sue 键的哈希值为 7291, mod 5 的结果为 1,将 Sue 的数据存进 1 号箱中。

Dan 键的哈希值为 1539, mod 5 的结果为 4,将 Dan 的数据存进 4 号箱中。

Nell 键的哈希值为 6276, mod 5 的结果为 1,本应将其存进数组的 1 号箱中,但此时 1 号箱中已经存储了 Sue 的数据,这种存储位置重复了的情况便叫作冲突

遇到这种情况,可使用链表在已有数据的后面继续存储新的数据(链表法)。

Ally 键的哈希值为 9143, mod 5 的结果为 3,本应将其存储在数组的 3 号箱中,但 3 号箱中已经有了 Joe 的数据,所以使用链表,在其后面存储 Ally 的数据。

Bob 键的哈希值为 5278, mod 5 的结果为 3,本应将其存储在数组的 3 号箱中,但 3 号箱中已经有了 Joe 和 Ally 的数据,所以使用链表,在 Ally 的后面继续存储 Bob 的数据。

像这样存储完所有数据,哈希表也就制作完成了。

接下来讲解数据的查询方法,假设我们要查询 Dan 的性别。

为了知道 Dan 存储在哪个箱子里,首先需要算出 Dan 键的哈希值,然后对其进行 mod 运算,最后得到的结果为 4,于是我们知道了它存储在 4 号箱中。

查看 4 号箱可知,其中的数据的键与 Dan 一致,于是取出对应的值,由此我们便知道了 Dan 的性别为男(M)。

那么,想要查询 Ally 的性别时该怎么做呢?为了找到它的存储位置,先要算出 Ally 键的哈希值,再对其进行 mod 运算,最终得到的结果为 3。

然而 3 号箱中数据的键是 Joe 而不是 Ally,此时便需要对 Joe 所在的链表进行线性查找。

于是我们找到了键为 Ally 的数据,取出其对应的值,便知道了 Ally 的性别为女(F)。

哈希冲突

在哈希表中,我们可以利用哈希函数快速访问到数组中的目标数据。如果发生哈希冲突,就使用链表进行存储,这样一来,不管数据量为多少,我们都能够灵活应对。

如果数组的空间太小,使用哈希表的时候就容易发生冲突,线性查找的使用频率也会更高;反过来,如果数组的空间太大,就会出现很多空箱子,造成内存的浪费。因此,给数组设定合适的空间非常重要。

在存储数据的过程中,如果发生冲突,可以利用链表在已有数据的后面插入新数据来解决冲突,这种方法被称为链表法,也被称为链地址法

其中在 Java 集合类的 HashMap 中解决冲突的方法就是采用的链表法,建议阅读 HashMap 源码。

除了链地址法以外,还有几种解决冲突的方法。其中,应用较为广泛的是开放地址法,或称为开放寻址法。这种方法是指当冲突发生时,立刻计算出一个候补地址(数组上的位置)并将数据存进去。如果仍然有冲突,便继续计算下一个候补地址,直到有空地址为止,可以通过多次使用哈希函数线性探测法等方法计算候补地址。

在 Java 中,ThreadLocal 所使用的就是开放地址法

哈希函数设计的好坏决定了哈希冲突的概率,也就决定哈希表的性能。

总结

这篇文章主要讲了一些比较基础的哈希表知识,包括哈希表的由来、哈希冲突的解决方法。

哈希表也叫散列表,来源于数组,它借助哈希函数对数组这种数据结构进行扩展,利用的是数组支持按照下标随机访问元素的特性,是存储 Key-Value 映射的集合。

哈希表两个核心问题是哈希函数设计和哈希冲突解决。对于某一个 Key,哈希表可以在接近 O(1) 的时间内进行读写操作。哈希表通过哈希函数实现 Key 和数组下标的转换,通过开放寻址法和链表法来解决哈希冲突。哈希函数设计的好坏决定了哈希冲突的概率,也就决定哈希表的性能。

有兴趣的可以在 JDK 中阅读 HashMap 的源码,在 JDK 8 和之前的版本的实现还有许多不多,比如在 JDK 8 中,引入红黑树,当链表长度太长(默认超过 8)时,链表就转换为红黑树,就可以利用红黑树快速增删改查的特点,提高 HashMap 的性能。

参考

《我的第一本算法书》

https://github.com/wupeixuan/JDKSourceCode1.8

Guess you like

Origin www.cnblogs.com/wupeixuan/p/12319785.html