hash_map采用separate chaining解决冲突,每个key值会通过hash函数映射到对应的bucket(通过vector实现),如果两个key值的hash值相等,那么这个bucket中会放入多个元素,通过next指针做链接。
hash_map通过hashtable来实现:
template
class hash_map
{
private:
typedef hashtable,_Key,_HashFcn,
_Select1st >,_EqualKey,_Alloc> _Ht;
_Ht _M_ht;
}
template
struct _Hashtable_node //保存元素
{
_Hashtable_node* _M_next; //解决冲突
_Val _M_val;
};
hasher _M_hash; //key值做hash
key_equal _M_equals; //查找比较key相等,需要我们自行重载key对象operator==
_ExtractKey _M_get_key; //取出key
vector<_Node*,_Alloc> _M_buckets; //桶
size_type _M_num_elements; //总的元素个数
下面是bukets的创建
hashtable(size_type __n,
const _HashFcn& __hf,
const _EqualKey& __eql,
const _ExtractKey& __ext,
const allocator_type& __a = allocator_type())
: __HASH_ALLOC_INIT(__a)
_M_hash(__hf),
_M_equals(__eql),
_M_get_key(__ext),
_M_buckets(__a),
_M_num_elements(0)
{
_M_initialize_buckets(__n);//hash_map默认传过来100
}
void _M_initialize_buckets(size_type __n)
{
const size_type __n_buckets = _M_next_size(__n); //获取buckets个数
_M_buckets.reserve(__n_buckets);
_M_buckets.insert(_M_buckets.end(), __n_buckets, (_Node*) 0);
_M_num_elements = 0;
}
下面是获取bucket个数:
enum { __stl_num_primes = 28 };
static const unsigned long __stl_prime_list[__stl_num_primes] =
{
53ul, 97ul, 193ul, 389ul, 769ul,
1543ul, 3079ul, 6151ul, 12289ul, 24593ul,
49157ul, 98317ul, 196613ul, 393241ul, 786433ul,
1572869ul, 3145739ul, 6291469ul, 12582917ul, 25165843ul,
50331653ul, 100663319ul, 201326611ul, 402653189ul, 805306457ul,
1610612741ul, 3221225473ul, 4294967291ul
};
inline unsigned long __stl_next_prime(unsigned long __n)
{
const unsigned long* __first = __stl_prime_list;
const unsigned long* __last = __stl_prime_list + (int)__stl_num_primes;
const unsigned long* pos = lower_bound(__first, __last, __n); //必须为有序才能lower_bound
return pos == __last ? *(__last - 1) : *pos; //获取最大的适当值
}
pair insert_unique(const value_type& __obj)
{
resize(_M_num_elements + 1);
return insert_unique_noresize(__obj);
}
const size_type __old_n = _M_buckets.size();
if (__num_elements_hint > __old_n) {
const size_type __n = _M_next_size(__num_elements_hint); //重新计算bucket大小
if (__n > __old_n) { //是否需要处理,相等则略过处理
vector<_Node*, _All> __tmp(__n, (_Node*)(0),
_M_buckets.get_allocator()); //vector重新resize赋值0
__STL_TRY {
for (size_type __bucket = 0; __bucket < __old_n; ++__bucket) {//处理原来的每一个bucket,拷贝到新的正确的bucket中
_Node* __first = _M_buckets[__bucket];
while (__first) {
size_type __new_bucket = _M_bkt_num(__first->_M_val, __n);//计算到哪一个新的bucket
_M_buckets[__bucket] = __first->_M_next; //移动bucket指针到下一个,进行下一个while处理
__first->_M_next = __tmp[__new_bucket]; //next指向新bucket的第一个元素
__tmp[__new_bucket] = __first; //每次都是把这个元素赋给新的bucket的第一个元素
__first = _M_buckets[__bucket];
}
}
_M_buckets.swap(__tmp); //处理好的新的tmp最后给bucket
}
# ifdef __STL_USE_EXCEPTIONS
catch(...) {
for (size_type __bucket = 0; __bucket < __tmp.size(); ++__bucket) {
while (__tmp[__bucket]) {
_Node* __next = __tmp[__bucket]->_M_next;
_M_delete_node(__tmp[__bucket]);
__tmp[__bucket] = __next;
}
}
throw;
}
# endif /* __STL_USE_EXCEPTIONS */
}
}
hashtable<_Val,_Key,_HF,_Ex,_Eq,_All>
::insert_unique_noresize(const value_type& __obj)
{
const size_type __n = _M_bkt_num(__obj);
_Node* __first = _M_buckets[__n];
for (_Node* __cur = __first; __cur; __cur = __cur->_M_next)
if (_M_equals(_M_get_key(__cur->_M_val), _M_get_key(__obj)))//遍历这个bucket判断key相等
return pair(iterator(__cur, this), false);
_Node* __tmp = _M_new_node(__obj);
__tmp->_M_next = __first;
_M_buckets[__n] = __tmp;
++_M_num_elements;
return pair(iterator(__tmp, this), true);
}
回顾之前的hash_map定义
typedef hashtable,_Key,_HashFcn,
_Select1st >,_EqualKey,_Alloc> _Ht;
为什么这么处理是因为hash_set也是通过hashtable来实现,_Extractkey直接取的value
template
class hash_set
{
private:
typedef hashtable<_Value, _Value, _HashFcn, _Identity<_Value>,
_EqualKey, _Alloc> _Ht;
_Ht _M_ht;
};
struct _Hashtable_iterator {
_Node* _M_cur; //当前位置
_Hashtable* _M_ht; //保存hashtable
};
_Hashtable_iterator<_Val,_Key,_HF,_ExK,_EqK,_All>&
_Hashtable_iterator<_Val,_Key,_HF,_ExK,_EqK,_All>::operator++()
{
const _Node* __old = _M_cur;
_M_cur = _M_cur->_M_next;
if (!_M_cur) { //需要在下一个bucket查找
size_type __bucket = _M_ht->_M_bkt_num(__old->_M_val);
while (!_M_cur && ++__bucket < _M_ht->_M_buckets.size())
_M_cur = _M_ht->_M_buckets[__bucket];
}
return *this;
}
hashtable的begin和end
iterator begin()
{
for (size_type __n = 0; __n < _M_buckets.size(); ++__n)
if (_M_buckets[__n]) //查找第一个不为空的bucket
return iterator(_M_buckets[__n], this);
return end();
}
const_iterator end() const { return const_iterator(0, this); } //node为空表示end
注:hash_map不属于c++的标准容器,c++11提供了类型的hash容器unordered_map