STL中的hash_map使用

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/PROGRAM_anywhere/article/details/78630910

主要分两部分来使用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 <iostream> 
#include <hash_map>
#include <string>
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<<h.first<<"->"<<h.second<<endl;
    }
}
/*
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 hashtable<pair<const Key, T>, Key, HashFcn,
            select1st<pair<const 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<const char*>()(s.c_str()); }//这个也行 不过注意模板先要实例化才能调用() 
    };
}
void test2(){
    __gnu_cxx::hash_map<string, int> hm;
    ++hm["帅"];
    ++hm["东"];
    ++hm["帅"];
    for(auto h : hm){
        cout<<h.first<<"->"<<h.second<<endl;
    }
}
/*
东->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<<h.first<<"->"<<h.second<<endl;
    }
}

现在有来一个需求:要求你统计文本中的单词个数,并且单词忽略大小写
这个时候就要牵涉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<<x<<" compare to "<<y<<endl;
        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<<h.first<<"->"<<h.second<<endl;
    }
}
/*
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_function<T, T, bool> {
    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

展开阅读全文

没有更多推荐了,返回首页