POCO C++库学习和分析哈希.docx
《POCO C++库学习和分析哈希.docx》由会员分享,可在线阅读,更多相关《POCO C++库学习和分析哈希.docx(25页珍藏版)》请在冰豆网上搜索。
POCOC++库学习和分析哈希
POCOC++库学习和分析--哈希
1.Hash概论
在理解Poco中的Hash代码之前,首先需要了解一下Hash的基本理论。
下面的这些内容和教课书上的内容并没有太大的差别。
1.1定义
下面这几段来自于XX百科:
Hash:
一般翻译做"散列",也有直接音译为"哈希"的,就是把任意长度的输入(又叫做预映射,pre-image),通过散列算法,变换成固定长度的输出,该输出就是散列值。
这种转换是一种压缩映射,也就是,散列值的空间通常远小于输入的空间,不同的输入可能会散列成相同的输出,而不可能从散列值来唯一的确定输入值。
简单的说就是一种将任意长度的消息压缩到某一固定长度的消息摘要的函数。
Hashtable:
散列表,也叫哈希表,是根据关键码值(Keyvalue)而直接进行访问的数据结构。
也就是说,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度。
这个映射函数叫做散列函数,存放记录的数组叫做散列表。
*若结构中存在关键字和K相等的记录,则必定存储在f(K)的位置上。
由此,不需比较便可直接取得所查记录。
这个对应关系f称为散列函数(Hashfunction),按这个思想建立的表为散列表。
*对不同的关键字可能得到同一散列地址,即key1≠key2,而f(key1)=f(key2),这种现象称冲突。
具有相同函数值的关键字对该散列函数来说称做同义词。
*综上所述,根据散列函数H(key)和处理冲突的方法将一组关键字映象到一个有限的连续的地址集(区间)上,并以关键字在地址集中的“象”,作为这条记录在表中的存储位置,这种表便称为散列表,这一映象过程称为散列造表或散列,所得的存储位置称散列地址。
这个现象也叫散列桶,在散列桶中,只能通过顺序的方式来查找,一般只需要查找三次就可以找到。
科学家计算过,当重载因子不超过75%,查找效率最高。
*若对于关键字集合中的任一个关键字,经散列函数映象到地址集合中任何一个地址的概率是相等的,则称此类散列函数为均匀散列函数(UniformHashfunction),这就是使关键字经过散列函数得到一个“随机的地址”,从而减少冲突。
1.2Hashtable查找效率
对于Hashtable来言,理论上查找效率为O
(1)。
但在现实世界中,查找的过程存在冲突现象。
产生的冲突少,查找效率就高,产生的冲突多,查找效率就低。
因此,影响产生冲突多少的因素,也就是影响查找效率的因素。
影响产生冲突多少有以下三个因素:
1.散列函数是否均匀;
2.处理冲突的方法;
3.散列表的装填因子。
散列表的装填因子定义为:
α=填入表中的元素个数/散列表的长度
实际上,散列表的平均查找长度是装填因子α的函数,只是不同处理冲突的方法有不同的函数。
1.3Poco中的Hash内容
Poco中的hash内容主要关注于Hash表的应用。
下面是Poco中相关于Hash的类图:
我们看到Poco的Hash内容主要被分成3部分:
1.Hash函数。
Poco提供了一组Hash函数用于,生成hash值。
同时提供了模板类HashFunction,通过仿函式提供对任意数据结构生成hash值的功能。
2.Hashtable(哈希表)。
Poco中实现了3种哈希表,分别是SimpleHashTable,HashTable,LinearHashTable。
3.在哈希表上的应用,封装出hashmap和hashset。
2.Hash函数
Hash函数是解决hash冲突的第一个要素。
Poco中提供了一组Hash函数,用于产生hash值。
其定义如下:
[cpp]viewplaincopy
inlinestd:
:
size_thash(Int8n)
{
returnstatic_cast:
size_t>(n)*2654435761U;
}
inlinestd:
:
size_thash(UInt8n)
{
returnstatic_cast:
size_t>(n)*2654435761U;
}
inlinestd:
:
size_thash(Int16n)
{
returnstatic_cast:
size_t>(n)*2654435761U;
}
inlinestd:
:
size_thash(UInt16n)
{
returnstatic_cast:
size_t>(n)*2654435761U;
}
inlinestd:
:
size_thash(Int32n)
{
returnstatic_cast:
size_t>(n)*2654435761U;
}
inlinestd:
:
size_thash(UInt32n)
{
returnstatic_cast:
size_t>(n)*2654435761U;
}
inlinestd:
:
size_thash(Int64n)
{
returnstatic_cast:
size_t>(n)*2654435761U;
}
inlinestd:
:
size_thash(UInt64n)
{
returnstatic_cast:
size_t>(n)*2654435761U;
}
std:
:
size_thash(conststd:
:
string&str)
{
std:
:
size_th=0;
std:
:
string:
:
const_iteratorit=str.begin();
std:
:
string:
:
const_iteratorend=str.end();
while(it!
=end)
{
h=h*0xf4243^*it++;
}
returnh;
}
这里就不对hash函数做过多叙述了,下面列出一些其他的常用hash函数。
网上有专门的论述,并对不同的hash函数效果做了比较,有兴趣的话可以google一下。
附:
各种哈希函数的C语言程序代码
[cpp]viewplaincopy
unsignedintSDBMHash(char*str)
{
unsignedinthash=0;
while(*str)
{
//equivalentto:
hash=65599*hash+(*str++);
hash=(*str++)+(hash<<6)+(hash<<16)-hash;
}
return(hash&0x7FFFFFFF);
}
//RSHash
unsignedintRSHash(char*str)
{
unsignedintb=378551;
unsignedinta=63689;
unsignedinthash=0;
while(*str)
{
hash=hash*a+(*str++);
a*=b;
}
return(hash&0x7FFFFFFF);
}
//JSHash
unsignedintJSHash(char*str)
{
unsignedinthash=1315423911;
while(*str)
{
hash^=((hash<<5)+(*str++)+(hash>>2));
}
return(hash&0x7FFFFFFF);
}
//P.J.WeinbergerHash
unsignedintPJWHash(char*str)
{
unsignedintBitsInUnignedInt=(unsignedint)(sizeof(unsignedint)*8);
unsignedintThreeQuarters=(unsignedint)((BitsInUnignedInt*3)/4);
unsignedintOneEighth=(unsignedint)(BitsInUnignedInt/8);
unsignedintHighBits=(unsignedint)(0xFFFFFFFF)<<(BitsInUnignedInt-OneEighth);
unsignedinthash=0;
unsignedinttest=0;
while(*str)
{
hash=(hash<if((test=hash&HighBits)!
=0)
{
hash=((hash^(test>>ThreeQuarters))&(~HighBits));
}
}
return(hash&0x7FFFFFFF);
}
//ELFHash
unsignedintELFHash(char*str)
{
unsignedinthash=0;
unsignedintx=0;
while(*str)
{
hash=(hash<<4)+(*str++);
if((x=hash&0xF0000000L)!
=0)
{
hash^=(x>>24);
hash&=~x;
}
}
return(hash&0x7FFFFFFF);
}
//BKDRHash
unsignedintBKDRHash(char*str)
{
unsignedintseed=131;//31131131313131131313etc..
unsignedinthash=0;
while(*str)
{
hash=hash*seed+(*str++);
}
return(hash&0x7FFFFFFF);
}
//DJBHash
unsignedintDJBHash(char*str)
{
unsignedinthash=5381;
while(*str)
{
hash+=(hash<<5)+(*str++);
}
return(hash&0x7FFFFFFF);
}
//APHash
unsignedintAPHash(char*str)
{
unsignedinthash=0;
inti;
for(i=0;*str;i++)
{
if((i&1)==0)
{
hash^=((hash<<7)^(*str++)^(hash>>3));
}
else
{
hash^=(~((hash<<11)^(*str++)^(hash>>5)));
}
}
return(hash&0x7FFFFFFF);
}
unsignedinthash(char*str)
{
registerunsignedinth;
registerunsignedchar*p;
for(h=0,p=(unsignedchar*)str;*p;p++)
h=31*h+*p;
returnh;
}
[cpp]viewplaincopy
//PHP中出现的字符串Hash函数
staticunsignedlonghashpjw(char*arKey,unsignedintnKeyLength)
{
unsignedlongh=0,g;
char*arEnd=arKey+nKeyLength;
while(arKeyh=(h<<4)+*arKey++;
if((g=(h&0xF0000000))){
h=h^(g>>24);
h=h^g;
}
}
returnh;
}
[cpp]viewplaincopy
//OpenSSL中出现的字符串Hash函数
unsignedlonglh_strhash(char*str)
{
inti,l;
unsignedlongret=0;
unsignedshort*s;
if(str==NULL)return(0);
l=(strlen(str)+1)/2;
s=(unsignedshort*)str;
for(i=0;i
ret^=(s[i]<<(i&0x0f));
return(ret);
}
/*Thefollowinghashseemstoworkverywellonnormaltextstrings
*nocollisionson/usr/dict/wordsanditdistributeson%2^nquite
*well,notasgoodasMD5,butstillgood.
*/
unsignedlonglh_strhash(constchar*c)
{
unsignedlongret=0;
longn;
unsignedlongv;
intr;
if((c==NULL)||(*c=='\0'))
return(ret);
/*
unsignedcharb[16];
MD5(c,strlen(c),b);
return(b[0]|(b[1]<<8)|(b[2]<<16)|(b[3]<<24));
*/
n=0x100;
while(*c)
{
v=n|(*c);
n+=0x100;
r=(int)((v>>2)^v)&0x0f;
ret=(ret(32-r));
ret&=0xFFFFFFFFL;
ret^=v*v;
c++;
}
return((ret>>16)^ret);
}
[cpp]viewplaincopy
//MySql中出现的字符串Hash函数
#ifndefNEW_HASH_FUNCTION
/*Calchashvalueforakey*/
staticuintcalc_hashnr(constbyte*key,uintlength)
{
registeruintnr=1,nr2=4;
while(length--)
{
nr^=(((nr&63)+nr2)*((uint)(uchar)*key++))+(nr<<8);
nr2+=3;
}
return((uint)nr);
}
/*Calchashvalueforakey,caseindepenently*/
staticuintcalc_hashnr_caseup(constbyte*key,uintlength)
{
registeruintnr=1,nr2=4;
while(length--)
{
nr^=(((nr&63)+nr2)*((uint)(uchar)toupper(*key++)))+(nr<<8);
nr2+=3;
}
return((uint)nr);
}
#else
/*
*Fowler/Noll/Vohash
*
*Thebasisofthehashalgorithmwastakenfromanideasentbyemailtothe
*IEEEPosixP1003.2mailinglistfromPhongVo(kpv@)and
*GlennFowler(gsf@).LandonCurtNoll(chongo@)
*laterimprovedontheiralgorithm.
*
*Themagicisintheinterestingrelationshipbetweenthespecialprime
*16777619(2^24+403)and2^32and2^8.
*
*Thishashproducesthefewestcollisionsofanyfunctionthatwe'veseenso
*far,andworkswellonbothnumbersandstrings.
*/
uintcalc_hashnr(constbyte*key,uintlen)
{
constbyte*end=key+len;
uinthash;
for(hash=0;key{
hash*=16777619;
hash^=(uint)*(uchar*)key;
}
return(hash);
}
uintcalc_hashnr_caseup(constbyte*key,uintlen)
{
constbyte*end=key+len;
uinthash;
for(hash=0;key{
hash*=16777619;
hash^=(uint)(uchar)toupper(*key);
}
return(hash);
}
#endif
3.Hash表
我们接下去分析Poco中Hash表的实现。
Poco中实现了3种哈希表,分别是SimpleHashTable,HashTable,LinearHashTable。
它们的实现对应了当出现冲突时,解决冲突的不同方法。
首先我们看一下通用的解决方法。
1.线性探测。
当出现碰撞时,顺序依次查询后续位置,直到找到空位。
《利用线性探测法构造散列表》
2.双重散列法。
当使用第一个散列Hash函数,出现碰撞时,用第二个散列函数去寻找空位
3.拉链法。
出现碰撞的时候,使用list存储碰撞数据
4.线性哈希,linearhash。
立刻分裂或者延迟分裂。
通过分裂,控制桶的高度,每次分裂时,会重新散列碰撞元素。
《linearhashing》
SimpleHashTable的实现对应了方法一;HashTable对应了方法3;LinearHashTable对应了方法4。
3.1SimpleHashTable
从类图里我们看到,SimpleHashTable是一个HashEntry容器,内部定义如下:
[cpp]viewplaincopy
std:
:
vector_entries
当插入新数据时,首先根据hash值,计算空位,然后存储;如果发现冲突,顺着计算的hash值按地址顺序依次寻找空位;如_entries容器无空位,则抛出异常。
[cpp]viewplaincopy
UInt32insert(constKey&key,constValue&value)
///Returnsthehashvalueoftheinserteditem.
///Throwsanexceptioniftheentrywasalreadyinserted
{
UInt32hsh=hash(key);
insertRaw(key,hsh,value);
returnhsh;
}
Value&insertRaw(constKey&key,UInt32hsh,constValue&value)
///Returnsthehashvalueoftheinserteditem.
///Throwsanexceptioniftheentrywasalreadyinserted
{
UInt32pos=hsh;
if(!
_entries[pos])
_entries[pos]=newHashEntry(key,value);
else
{
UInt32origHash=hsh;
while(_entries[hsh%_capacity])
{
if(_entries[hsh%_capacity]->key==key)
throwExistsException();
if(hsh-origHash>_capacity)
throwPoolOverflowException("SimpleHashTablefull");
hsh++;
}
pos=hsh%_capacity;
_entries[pos]=newHashEntry(key,value);
}
_size++;
return_entries[pos]->value;
}
SimpleHashTable进行搜索时,策略也一致。
[cpp]viewplaincopy
constValue&get(constKey&key)const
///Throwsanexceptionifthevaluedoesnotexist
{
UInt32hsh=hash(key);
returngetRaw(key,hsh);
}
constValue&g