hash_map容器

hash_map采用separate chaining解决冲突,每个key值会通过hash函数映射到对应的bucket(通过vector实现),如果两个key值的hash值相等,那么这个bucket中会放入多个元素,通过next指针做链接。

hash_map通过hashtable来实现:

template <class _Key, class _Tp, class _HashFcn, class _EqualKey,
          class _Alloc>
class hash_map
{
private:
  typedef hashtable<pair<const _Key,_Tp>,_Key,_HashFcn,
                    _Select1st<pair<const _Key,_Tp> >,_EqualKey,_Alloc> _Ht;
  _Ht _M_ht;
}

因此主要来看hashtable的实现:

template <class _Val>
struct _Hashtable_node        //保存元素
{
  _Hashtable_node* _M_next;  //解决冲突
  _Val _M_val;
}; 

hashtable成员变量

  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; //获取最大的适当值
}

hashtable的插入:

  pair<iterator, bool> insert_unique(const value_type& __obj)
  {
    resize(_M_num_elements + 1);
    return insert_unique_noresize(__obj);
  }

resize会根据元素个数来重新调整bucket的大小,即vector会重新计算

  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 */
    }
  }

处理好bucket后进行插入

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, bool>(iterator(__cur, this), false);

  _Node* __tmp = _M_new_node(__obj);
  __tmp->_M_next = __first;
  _M_buckets[__n] = __tmp;
  ++_M_num_elements;
  return pair<iterator, bool>(iterator(__tmp, this), true);
}

这里需要理清楚的是bucket里存储的是value,那么hash_map的key存储在哪里呢?

回顾之前的hash_map定义

  typedef hashtable<pair<const _Key,_Tp>,_Key,_HashFcn,
                    _Select1st<pair<const _Key,_Tp> >,_EqualKey,_Alloc> _Ht;

所以hashtable里的value实际是hash_map里的key和value构成的pair,而_Select1st取出pair里的key,即hashtable里的_Extractkey。

为什么这么处理是因为hash_set也是通过hashtable来实现,_Extractkey直接取的value

template <class _Value, class _HashFcn, class _EqualKey, class _Alloc>
class hash_set
{
private:
  typedef hashtable<_Value, _Value, _HashFcn, _Identity<_Value>, 
                    _EqualKey, _Alloc> _Ht;
  _Ht _M_ht;
};

剩下看一下hashtable的迭代器

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

同时hashtable也提供了insert_equal方法,用于插入相同的元素,以此实现hash_multimap和hash_multiset

注:hash_map不属于c++的标准容器,c++11提供了类型的hash容器unordered_map

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值