总览
- Hash(哈希、散列)
Hash是一种
散列函数或方法
的统称。
·
该方法就是:把任意长度的输入通过散列算法变换成固定长度的输出,该输出就是散列值。—— (散列方法)
·
这种转换是一种压缩映射,也就是,散列值的空间通常远小于输入的空间,不同的输入可能会散列成相同的输出,所以不可能从散列值来确定唯一的输入值。简单的说就是一种将任意长度的消息压缩到某一固定长度的消息摘要的函数。
Hash技术应用于
符号表
,用于构建符号表的结构、属性填入及查找。
·
符号表: 存储用户标识符及其属性信息。
之后将以符号表为例进行解析。
一、Hash函数(哈希函数、散列函数)
假定存在一个有限区域,这个区域要填写一张含N项符号的符号表。通常希望构造一个地址函数(假定函数名为Hash,用于确定符号表中存放符号的地址),对于编译器而言,任何符号(标识符)都有其对应的编号SYM,Hash( SYM ) 则返回一个 0 ~ N-1 间的数,以便将符号能放进符号表的限定空间内。符号表的填表和查表都依赖于 Hash( SYM ) 来获取符号的位置。这里 Hash( SYM ) 被定义为 SYM % N
,即函数返回 SYM / N 的余数
。该值随着SYM的变化,始终在0 ~ N-1 之间变化。可以说随着SYM的变化,Hash函数返回值的集合始终在0 ~ N-1 的区间内散列分布,因此该函数被称为散列函数,英文为Hash函数,Hash函数的返回值被称为Hash值(散列值)。
对Hash函数的要求:
- 函数的计算要简单、高效;
- 函数值能比较均匀地分布在 0 ~ N-1 之间。
构造Hash函数的方法有很多,通常是将符号名的编码散列为 0 ~ N-1 之间的某一个值。由于用户使用标识符是随机的,而且标识符的个数也是无限的,因此,企图构造一 一对应的函数是徒劳的。在这种情况下,除了希望函数值的分布比较均匀外,还应设法解决“地址冲突”的问题。
例如:
N = 17,Hash( SYM ) 为 SYM / N 的余数时,由于Hash( ‘05’ ) = Hash( ‘22’) = 5,此时便存在地址冲突。
符号表将Hash值相同的符号以存储时间的先后顺序,后存储的指向先存储的元素将其链接在一起。
二、Hash值(哈希值、散列值)
由以上Hash函数的介绍可知:
Hash值由Hash函数产生,即为Hash函数的返回值,是待存储于某区间或已存储于某区间的数据的唯一编号经过Hash函数计算之后的值,该值(Hash值)散列在目的存储区间大小的范围之内(如 0 ~ N-1 之间)。
在目的区间与Hash表建立映射关系之后,该区间存储地址便与Hash值建立起了映射关系。注意该映射关系不是一 一对应的关系,因为可能存在不同的数据Hash值相同的情况。
三、HashTable(哈希表)
Hash表是根据关键码值(Key value)而直接进行访问的数据结构。也就是说,它通过把关键码值(SYM)映射到表中一个位置来访问记录,以加快查找的速度。
将所有符号的哈希值对应于一个数组(表)的下标,成为哈希表。该表成为符号表中符号元素的索引表。其Hash值链接到该Hash值对应的符号表中的符号元素。Hash表是一个容量为N的一维数组,它的每个元素初值为null。
对于符号元素的存储和查找,都通过Hash表进行索引,先通过Hash函数计算得到其Hash值,然后在Hash表中找到该Hash值的位置,从Hash值的链接进入符号表,找到元素对应存储位置。
四、HashMap(哈希映射)
HashMap即哈希映射,指的是Hash表中Hash值向符号表元素的映射过程,是一种基于散列算法实现的快速查找的键值对结构。底层实现是链表和数组。即Hash值、Hash表与符号表的映射关系或结构。
Hash技术使用一张一维数组(Hash表)为首的链表通过间接方式来查填符号表。将相同Hash值的符号采用链表的方式连成一串,便于线性查找。符号表则将Hash值相同的符号以存储时间的先后顺序,后存储的指向先存储的元素将其链接在一起。Hash表的Hash值则指向其对应符号表中最后存储的该Hash值的符号。
HashMap数组的每一个元素不止是一个Entry对象,也是一个链表的头节点。每一个Entry对象通过Next指针指向它的下一个Entry节点。当新来的Entry映射到冲突的数组位置时,只需要插入到对应的链表即可:
需要注意的是,新来的Entry节点插入链表时,使用的是“头插法”。至于为什么不插入链表尾部,是因为,后插入的Entry被查找的可能性更大,放到链表头部有利于提高程序性能。