主要分两部分来使用hash_map
1.针对 key = int char 等内置类型
2.针对 key = 非内置类型
部分源码全部来自于sgi-v2.03版
都知道要使用hashtable必须有hash函数,由于STL内核提供了如下:
内置的HashFcn:
struct hash<char*>
struct hash<const char*>
struct hash<char>
struct hash<unsigned char>
struct hash<signed char>
struct hash<short>
struct hash<unsigned short>
struct hash<int>
struct hash<unsigned int>
struct hash<long>
struct hash<unsigned long>
所以当使用key = char 、const char 、char、unsigned char、signed char、short、unsigned short、int、unsigned int、long、unsigned long。都不需要自定义hash函数
例子
#include
#include
#include
using namespace std;
void test1(){
__gnu_cxx::hash_map<int, string> hm;//windows下面是在 std命名空间中
hm[4] = "帅";
hm[2] = "帅";
hm[3] = "东";
for(auto h : hm){
cout<"->"</*
2->帅
3->东
4->帅
*/
和map使用差不多
STL中的hash_map调用了hashtable
hash_map模板参数
template <class Key, class T, class HashFcn = hash<Key>,
class EqualKey = equal_to<Key>,
class Alloc = alloc>
hashtable模板参数:
template <class Value, class Key, class HashFcn,
class ExtractKey, class EqualKey,
class Alloc>
hash_map中创建了一个hashtable
typedef hashtableconst Key, T>, Key, HashFcn,
select1stconst Key, T> >, EqualKey, Alloc> ht;
所以上面hash_map<int, string> 等价于 hash_map<int, string, hash<int>, equal_to<int>, alloc>
//////////////////////////////////////////////////////////////////////////////
现在来了一个新的需求:要求你统计一个文本中每个汉字出现的次数,那么此时需要const char *当做key了,也可以使用内置的hashfcn但是我就使用string类吧,为了演示不是内置类型的key
1.利用模板的偏特化 这样做算是给stl内核代码添加功能了
namespace __gnu_cxx{
template<>
struct hash<string>{
size_t operator()(const string &s) const { return __stl_hash_string(s.c_str()); }
//size_t operator()(const string &s) const { return hash()(s.c_str()); }//这个也行 不过注意模板先要实例化才能调用()
};
}
void test2(){
__gnu_cxx::hash_map<string, int> hm;
++hm["帅"];
++hm["东"];
++hm["帅"];
for(auto h : hm){
cout<"->"</*
东->1
帅->2
*/
需要注意的就是gcc中hash_map在__gnu_cxx 命名空间中
__stl_hash_string 为stl内核 const char *的hash函数
inline size_t __stl_hash_string(const char* s){
unsigned long h = 0;
for ( ; *s; ++s)
h = 5*h + *s;
return size_t(h);
}
上面做不是太好,毕竟相当于给内核添加东西了,这样做还不如你自己去修改STL源码
这样做比较合适
class HashString{
public:
size_t operator()(const string &s) const {
return __gnu_cxx::__stl_hash_string(s.c_str());
}
};
void test3(){
__gnu_cxx::hash_map<string, int, HashString> hm;
++hm["帅"];
++hm["东"];
++hm["帅"];
for(auto h : hm){
cout<"->"<
现在有来一个需求:要求你统计文本中的单词个数,并且单词忽略大小写
这个时候就要牵涉equal_to函数了
template <class Key, class T, class HashFcn = hash<Key>,
class EqualKey = equal_to<Key>,
class Alloc = alloc>
equal_to的意义:有可能两个不同的key对应同一个桶 这时候就需要比较函数 是否需要保存新的key
所以现在不仅需要修改equal_to函数,hash函数也得改。
class HashString2{
public:
size_t operator()(const string &s) const {
/* 忽略大小写hash->全部当成小写进行hash */
int num = 0, size = s.size();
while(size--) {
num = 5 * num + s[size];
if(s[size] < 'a')
num += 'a' - 'A';
}
return num;
}
};
class MyStringEqual {
public:
bool operator()(const string &x, const string &y) const {
int size = x.size();
if(size != y.size())
return false;
cout<" compare to "<int diff = 'a' - 'A';
while(size--){
if(x[size] != y[size] &&
x[size] - diff != y[size] &&
x[size] + diff != y[size])
return false;
}
return true;
}
};
void test4(){
__gnu_cxx::hash_map<string, int, HashString2, MyStringEqual> hm;
++hm["shuai"];//如果没有初始化为0 ++后变为 1
++hm["dong"];
++hm["Dong"];//Dong hash结果和"dong" 一样,那么就会调用equal函数,如果不相等将采用链地址法保存 "Dong"这个key 如果相等那么将不保存"Dong"
++hm["DONG"];
++hm["SHUai"];
for(auto h : hm){
cout<"->"</*
dong compare to Dong
dong compare to DONG
shuai compare to SHUai
shuai->2
dong->3
*/
如果是自定义的类,重载==运算符就行了,由于我使用的是C++提供的string,所以不能修改
为什么重载了==就可以了呢?
因为内置key比较函数equal_to 默认调用的就是==
struct equal_to : public binary_functionbool> {
bool operator()(const T& x, const T& y) const { return x == y; }
};
有些编译器可能不需要加__gnu_cxx:: 比如vs,你去掉就好了
参考:
STL-v2.03源码
http://blog.csdn.net/u010025211/article/details/46653519