1. Hash概论
在理解Poco中的Hash代码之前,首先需要了解一下Hash的基本理论。下面的这些内容和教课书上的内容并没有太大的差别。
1.1 定义
下面这几段来自于百度百科:
Hash:一般翻译做"散列",也有直接音译为"哈希"的,就是把任意长度的输入(又叫做预映射, pre-image),通过散列算法,变换成固定长度的输出,该输出就是散列值。这种转换是一种压缩映射,也就是,散列值的空间通常远小于输入的空间,不同的输入可能会散列成相同的输出,而不可能从散列值来唯一的确定输入值。简单的说就是一种将任意长度的消息压缩到某一固定长度的消息摘要的函数。
Hash table:散列表,也叫哈希表,是根据关键码值(Key value)而直接进行访问的数据结构。也就是说,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度。这个映射函数叫做散列函数,存放记录的数组叫做散列表。
* 若结构中存在关键字和K相等的记录,则必定存储在f(K)的位置上。由此,不需比较便可直接取得所查记录。这个对应关系f称为散列函数(Hash function),按这个思想建立的表为散列表。
* 对不同的关键字可能得到同一散列地址,即key1≠key2,而f(key1)=f(key2),这种现象称冲突。具有相同函数值的关键字对该散列函数来说称做同义词。
* 综上所述,根据散列函数H(key)和处理冲突的方法将一组关键字映象到一个有限的连续的地址集(区间)上,并以关键字在地址集中的“象”, 作为这条记录在表中的存储位置,这种表便称为散列表,这一映象过程称为散列造表或散列,所得的存储位置称散列地址。这个现象也叫散列桶,在散列桶中,只能通过顺序的方式来查找,一般只需要查找三次就可以找到。科学家计算过,当重载因子不超过75%,查找效率最高。
* 若对于关键字集合中的任一个关键字,经散列函数映象到地址集合中任何一个地址的概率是相等的,则称此类散列函数为均匀散列函数(Uniform Hash function),这就是使关键字经过散列函数得到一个“随机的地址”,从而减少冲突。
1.2 Hash table查找效率
对于Hash table来言,理论上查找效率为O(1)。但在现实世界中,查找的过程存在冲突现象。产生的冲突少,查找效率就高,产生的冲突多,查找效率就低。因此,影响产生冲突多少的因素,也就是影响查找效率的因素。影响产生冲突多少有以下三个因素:
1. 散列函数是否均匀;
2. 处理冲突的方法;
3. 散列表的装填因子。
散列表的装填因子定义为:α= 填入表中的元素个数 / 散列表的长度
实际上,散列表的平均查找长度是装填因子α的函数,只是不同处理冲突的方法有不同的函数。
1.3 Poco中的Hash内容
Poco中的hash内容主要关注于Hash表的应用。下面是Poco中相关于Hash的类图:
我们看到Poco的Hash内容主要被分成3部分:
1. Hash函数。Poco提供了一组Hash函数用于,生成hash值。同时提供了模板类HashFunction,通过仿函式提供对任意数据结构生成hash值的功能。
2. Hash table(哈希表)。Poco中实现了3种哈希表,分别是SimpleHashTable, HashTable,LinearHashTable。
3. 在哈希表上的应用,封装出hash map和hash set。
2. Hash函数
Hash函数是解决hash冲突的第一个要素。
Poco中提供了一组Hash函数,用于产生hash值。其定义如下:
[cpp]
inline std::size_t hash(Int8 n)
{
return static_cast<std::size_t>(n)*2654435761U;
}
inline std::size_t hash(UInt8 n)
{
return static_cast<std::size_t>(n)*2654435761U;
}
inline std::size_t hash(Int16 n)
{
return static_cast<std::size_t>(n)*2654435761U;
}
inline std::size_t hash(UInt16 n)
{
return static_cast<std::size_t>(n)*2654435761U;
}
inline std::size_t hash(Int32 n)
{
return static_cast<std::size_t>(n)*2654435761U;
}
inline std::size_t hash(UInt32 n)
{
return static_cast<std::size_t>(n)*2654435761U;
}
inline std::size_t hash(Int64 n)
{
return static_cast<std::size_t>(n)*2654435761U;
}
inline std::size_t hash(UInt64 n)
{
return static_cast<std::size_t>(n)*2654435761U;
}
std::size_t hash(const std::string& str)
{
std::size_t h = 0;
std::string::const_iterator it = str.begin();
std::string::const_iterator end = str.end();
while (it != end)
{
h = h * 0xf4243 ^ *it++;
}
return h;
}
这里就不对hash函数做过多叙述了,下面列出一些其他的常用hash函数。网上有专门的论述,并对不同的hash函数效果做了比较,有兴趣的话可以google一下。
附:各种哈希函数的C语言程序代码
[cpp]
unsigned int SDBMHash(char *str)
{
unsigned int hash = 0;
while (*str)
{
// equivalent to: hash = 65599*hash + (*str++);
hash = (*str++) + (hash << 6) + (hash << 16) - hash;
}
return (hash & 0x7FFFFFFF);
}
// RS Hash
unsigned int RSHash(char *str)
{
unsigned int b = 378551;
unsigned int a = 63689;
unsigned int hash = 0;
while (*str)
{
hash = hash * a + (*str++);
a *= b;
}
return (hash & 0x7FFFFFFF);
}
// JS Hash
unsigned int JSHash(char *str)
{
unsigned int hash = 1315423911;
while (*str)
{
hash ^= ((hash << 5) + (*str++) + (hash >> 2));
}
return (hash & 0x7FFFFFFF);