前言
在C++98中,STL提供了底层为红黑树结构的一系列关联式容器,在查询时效率可达到 O(logN),即最差情况下需要比较红黑树的高度次,当树中的结点非常多时,查询效率也不理想。
在C++11中,STL又提供了4个unordered系列的关联式容器,这四个容器与红黑树结构的关联式容器使用方式基本类似,只是其底层结构不同,本文中只对unordered_map和unordered_set进行介绍。
一:unordered_map和unordered_set
unordered_map/unordered_set顾名思义:是无序的map和set,它们与map/set的基本使用大同小异,主要区别就是底层的数据结构。
unordered_map/unordered_set与map/set的区别:
- map和set的底层是用红黑树实现的(O(logN),unordered_map和unordered_set底层是用哈希表实现的(O(1))。
- map和set可以进行排序和去重,unordered_map和unordered_set可以进行去重,但不能排序。
- map和set是双向迭代器,unordered_map和unordered_set是单向迭代器。
unordered_map/unordered_set与map/set的关联:
- 它们都可以实现key和key/value的搜索场景,并且功能和使用基本相同。
二:哈希桶的改造
2.1 仿函数
在模板中提供了四个模板参数:template<class K, class T, class KeyofT, class Hash>
- key值类型
- 数据类型
- key值的获取方法
- hash(key)的获取方法
2.2 key值的获取方法
需要考虑到代码复用的问题,我们需要用一个哈希桶来实现K模型的unordered_set和KV模型的unordered_map。 并且对于某些自定义类型作为参数,我们也需要考虑从它的参数中获取key值,这时就需要增加一个模板参数,来让使用者自行提供从参数中获取key值的方法。
set中key值的获取:
struct SetKeyOfValue{
const K& operator()(const K& key){
return key;
}
};
map中key值的获取:
struct MapKeyOfValue{
const K& operator()(const std::pair<K, V>& kv){
return kv.first;
}
};
2.3 hash(key)的转换方法
若key的类型为整型,则可以直接使用哈希函数直接进行映射。但是如果key的类型是其他的一些无法进行整型算数运算的类型或者极为庞大的数据(如常用的string或者大数等类型) 就需要一种方法来将其转换为可以计算的整型值,但是对于自定义类型我们并不能知道他的转换方法,所以就需要提供一个仿函数,让使用者自行提供转换的方法。
常用的key一般都是string和int,这里我就给了默认的整型处理方法以及string的特化方法
// 默认
template<class K>
struct _Hash{
const K& operator()(const K& key){
return key;
}
};
// 特化
template<>
struct _Hash<std::string>{
size_t operator()(const std::string& key){
//BKDR字符串哈希函数
size_t hash = 0;
for (size_t i = 0; i < key.size(); i++){
hash *= 131;
hash += key[i];
}
return hash;
}
};
2.4 迭代器
如果迭代器当前所在的桶中的下一个位置不为空,则直接返回下一个位置。而如果下一个位置为空,则说明当前桶为空,就需要到下一个桶中遍历数据 。
那么我们又如何找到下一个桶的位置呢?
但是光光依靠迭代器是无法获取下一个桶的位置的,所以我就加入了一个哈希桶指针,这样就可以通过指针获取桶的哈希函数来计算出当前映射位置,再通过访问映射位置来找到下一个存有数据的桶,就可以计算出下一个位置。
template<class K, class T, class KeyOfT, class Hash>
struct __HashTableIterator{
typedef HashNode<T> Node;
typedef HashBucket<K, T, KeyOfT, Hash> HB;
typedef __HashTableIterator<K, T, KeyOfT, Hash> Self;
__HashTableIterator(Node* node, HB* hb)
: _node(node)
, _phb(hb)
{
}
T& operator*(){
return _node->_data;
}
T* operator->(){
return &_node->_data;
}
Self& operator++(){
//如果下一个节点不为空,直接返回下一个
if (_node->_next){
_node = _node->_next;
}
//如果下一个结点为空,则走到下一个桶中
else{
//通过获取当前数据的key来判断下一个数据的位置
KeyOfT koft;
size_t pos = _phb->HashFunc(koft(_node->_data));
++pos;
for (; pos < _phb->_table.size(); pos++){
Node* cur = _phb->_table[pos];
//如果下一个桶的数据不为空,则返回桶的第一个节点
if (cur != nullptr){
_node = cur;
return *this;
}
}
//剩下的桶都没有数据
_node = nullptr;
}
return *this;</