unordered_map和map类似,都是存储的key-value的值,可以通过key快速索引到value。
不同的是unordered_map不会根据key的大小进行排序,存储时是根据key的hash值判断元素是否相同,即unordered_map内部元素是无序的,而map中的元素是按照二叉搜索树存储,进行中序遍历会得到有序遍历。
嗯,总的来说,按照查找效率排名:unordered_map > hash_map > map;
我最近一个项目中,需要利用unordered_map进行字符串和int类型的映射,本来一开始使用unordered_map<string, int>即string作为key的键值的。但是在查找过程中,当传入一个字符串时,在底层它会复制一份到临时的string类型后再进行查找,这就产生了性能损失,由于项目需要尽可能好的性能,所以最终决定使用char *类型作为键值。
unordered_map的原型如下:
template<class Key, class Ty, class Hash = std::hash<Key>, class Pred = std::equal_to<Key>, class Alloc = std::allocator<std::pair<const Key, Ty> > > class unordered_map;>
其中Key是键类型。Ty是映射类型,也就是值。Hash是哈希函数对象类型,表示存储的过程中所使用的哈希函数,详细的情况可网上搜索哈希表结构。
Pred是相等比较函数对象类型,当查找时,如果传进来的字符串(假设这里是通过字符串作为key的)哈希后的哈希值与某个key的哈希值相同,将会进一步通过Pred函数进行比较判断,如果判断为真,则代表查找到了该值了。
使用const char*字符串作为键值的话,需要做两件事,一是重载比较函数Pred,二是重载Hash函数。
重载比较函数的原因是,如果使用它本身的比较函数std::equal_to<const char*>的话,它比较的仅仅只是两个指针的地址是否相同,而不是两个指针指向的字符串是否相同,因此需要重载使用strcmp进行判断,如下面的例子struct cmp。
重载哈希函数的原因也类似,因为如果使用hash<const char*>的话,存储时确实能正确地构造出一个哈希列表,但是它是用指针地址作为key值进行哈希的,而不是指针指向的字符串进行哈希,所以在传入一个字符串查找(比如使用iter = find(str)或者unorder_map[str])时,永远也查不到你需要的值。(本人在这里卡了很久,深深的噩梦啊)详细的可以看http://en.cppreference.com/w/cpp/utility/hash
链接里面有一句话十分关键!!十分关键!!就是下面代码里面注释的那句话!!
以下是我的代码:
struct cmp
{
bool operator()(const char *s1, const char *s2) const
{
return std::strcmp(s1, s2) == 0;
}
};
struct hash_func
{
size_t operator()(const char *str) const
{
int strLength = 0;
for (; *(str + strLength) != '\0'; ++strLength);
return std::_Hash_seq((unsigned char *)str, strLength );
}
};
/*There is no specialization for C strings.std::hash<const char*>
produces a hash of the value of the pointer(the memory address),
it does not examine the contents of any character array.*/
typedef std::unordered_map<const char*, int, hash_func, cmp> XMyMap;
XMyTable Sheet;
上面的hash_func是我自己跟踪进std::hash<string>里面,发现它是调用_Hash_seq的,于是在这里我依照它的情况使用了该函数,而不是按网上普遍的使用自己实现的哈希函数。_Hash_seq_()第一个参数是(unsigned char*)字符串指针,第二个参数是字符串的长度。
希望这篇文章能给后来有强迫症和性能癖的小伙伴们一点帮助^_^