由于哈希表的查找步骤与哈希函数都是恒定不变的,《180哈希2857开发8624》所以哈希表的时间复杂度为O(1)。
- 哈希函数
- 哈希函数是一种提取数据特征的算法,针对不通的数据形式有不同的哈希算法,所以哈希函数并不通用,针对不同场景有很多不同的哈希算法,比如我们常见的MD5就是一种获取文件信息摘要的哈希算法。
再比如在Java中,对于常用的数据类型,JDK就实现了多种不同的hash函数。
Integer:
public static int hashCode(int value) { return value; }
复制代码
Integer的哈希函数就是直接拿到它的值。
String:
public int hashCode() { int h = hash; if (h == 0 && value.length > 0) { hash = h = isLatin1() ? StringLatin1.hashCode(value) : StringUTF16.hashCode(value); } return h; }
复制代码
对于字符串类型则是使用了一个这样一种算法:s[0]31^(n-1) + s[1]31^(n-2) + ... + s[n-1]。
Double:
public static int hashCode(double value) { long bits = doubleToLongBits(value); return (int)(bits ^ (bits >>> 32)); }
复制代码
浮点类型则是使用位运算的方式进行哈希计算。
读者们可能会有疑惑,为什么要对这么多数据结构实现不同的哈希计算方法,这主要还是为了哈希值的均匀。
哈希值越均匀,就说明哈希函数设计的越好,也预示的哈希冲突的减少,关于哈希冲突,将在下节讲到。
在第一节的时候我说过,除了计算哈希值,我们还需要计算数组的位置。
计算数组位置有很多方法可用,这里我就介绍比较简单的除留余数法:
假设我对中国人这个key进行计算,得到了一个哈希值:19942986,那么我将用这个数字对数组的大小进行取余,这里我假设自己的数组大小是11,就得到这样的计算公式:
19942986 % 11 = 8
那么,我们就得出了本次哈希函数的结果为数组下标8,我们就可以将中国人这个字符串放到了数组下标8的位置上。
既然哈希值计算希望越均匀越好,那么数组下标是否也有类似的需求呢?
还真有,比如我们上面的除留余数法,在除留余数法中,数组大小的选择将深刻影响着取余结果是否均匀,所以在除留余数法中,我们一般使用质数,这也是为什么HashTable的初始化大小为11,因为11是一个质数。
- 哈希冲突
- 经过前文的学习后,相信大家对哈希表的相关概念都已经清楚了,那么本节就来学习哈希表的一大重点:哈希冲突。



