C++ HashMap

C++ HashMap

参考自boost库。

基本原理:
使用连续的内存块(数组)来存储数据,通过一个hash函数(即使每一个元素值都能形成固定的数组下标值),然后数组在相应的位置存起来,不能保证每个元素的关键值与hash函数值是一一对应的(可以有多个元素生成一样的hash值),这时候就需要解决冲突。
我们把数组叫做桶(因为数组把很多相似的元素存放在同一个位置)。
流程:
查找元素
- 元素的关键值通过hash函数计算得到hash值
- 得到桶号(数组下标位置)
- 比较桶的元素值(因为只有一次hash函数的值并不准确)
- 取出相同的元素的关键值,不相等则结束

插入元素
- 元素的关键值通过hash函数计算得到hash值
- 得到桶号(数组下标位置)
- 存放元素

我们需要一个提供hash函数的工具。写出一个行为类,重载()运算符。

class CalculateHashCode
{
public:
  std::size_t operator()(const string& str)
  {
    std::size_t n = 0;
    std::size_t nIndex = str.length();
    while (nIndex) {
      n <<= 5;
      if ((--nIndex)& 1) {
        n += str[nIndex] * 3;
      }
      else {
        n ^= ((n >> 17) | 0x01) * str[nIndex];
      }
    }
    return n;
  }
  std::size_t operator()(const wstring& str)
  {
    std::size_t n = 0;
    std::size_t nIndex = str.length();
    while (nIndex) {
      n <<= 5;
      if ((--nIndex) & 1) {
        n += str[nIndex];
      }
      else {
        n ^= ((n >> 17) | 0x01) * str[nIndex];
      }
    }
    return n;
  }
  std::size_t operator()(const short& nNum)
  {
    return nNum;
  }
  std::size_t operator()(const unsigned short& nNum)
  {
    return nNum;
  }
  std::size_t operator()(const char& nNum)
  {
    return nNum;
  }
  std::size_t operator()(const unsigned char& nNum)
  {
    return nNum;
  }
  std::size_t operator()(const size_t& nNum)
  {
    return nNum;
  }
  std::size_t operator()(const int& nNum)
  {
    return nNum;
  }
  std::size_t operator()(const long long& nNum)
  {
    return (std::size_t)(nNum >> 31 ^ nNum);
  }
  std::size_t operator()(const unsigned long long& nNum)
  {
    return (std::size_t)(nNum >> 31 ^ nNum);
  }
  std::size_t operator()(const float& nNum)
  {
    return (std::size_t)(nNum * 50331653.333333f);
  }
  std::size_t operator()(const double& nNum)
  {
    return (std::size_t)(nNum * 50331653.333333);
  }
  std::size_t operator()(const long double& nNum)
  {
    return (std::size_t)(nNum * 50331653.333333);
  }
};
HashMap:
hashMap应该是一个泛型容器,这里使用类模板来抽象数据类型。当然啦,也可以用void*来做出泛型。
template<typename Key, 
  typename Value, 
  typename CalcHash = CalculateHashCode>

三个类型参数名分别是元素的关键字,元素的内容值和计算hash的工具类型,这里我们默认为提供工具类型的是我们所写的计算hash类。

数据成员:

  typedef struct _tagBucket
  {
    iterator _first; //list某一个桶的开始
    iterator _last;  //list某一个桶的最后一个元素
  }Bucket;
  list<ValueType> _listValues;
  Bucket* _arrBuckets;
  std::size_t _nBucketSize;
  CalcHash _calcHash;

_listValues。使用标准库的双向链表来存放全部数据,为什么这样做呢?前面我们说过了当元素关键字冲突,计算到了一个被使用桶时候,这时候需要解决冲突问题,这里我们用将元素插入链表来解决,该方法称为链地址法。
_arrBuckets。 一个我们自定义的数据类型“Bucket”,我们只存放链表的区间范围的迭代器,这样就不需要为每一个桶都做一个链表,而是将每一个桶都想象为一个链表的某一个区间。
_nBucketSize。 桶的大小。
_calcHash。 计算hash值。

供外部使用的类型:

  typedef typename pair<Key, Value> ValueType;
  typedef typename list<ValueType>::iterator iterator;
  typedef typename  list<ValueType>::const_iterator const_iterator;

ValueType。标准库里,一对出现的元素都是用pair来解决的,我们也一样。
iterator。遍历链表元素。
const_iterator。遍历链表const元素。
内部方法:
我们将使用hash函数值的函数都是为私有的方法。

  iterator find(const Key& k, std::size_t nHashCode);
  Value& at(const Key& k, std::size_t nHashCode);
  pair<iterator, bool> insert(const ValueType& v, std::size_t nHashCode);
  void erase(iterator it, std::size_t nHashCode);
  void reHashSize(std::size_t nHashBucketSize);
  static std::size_t hashSize(std::size_t nNumber);//静态函数

find方法:通过一个元素关键字来查找,需要一个hash值,成功返回元素迭代器,失败放回end迭代器。
at方法:通过一个元素关键字来查找,需要一个hash值,成功返回元素的内容,失败插入一个相同关键字并初始化内容的元素,然后返回元素的内容。
insert:插入一个元素,需要一个hash值。不允许重复关键字,成功返回该元素迭代器,bool值为true,失败返回end迭代器,bool值为false。
erase:该元素的迭代器,需要一个hash值(需要知道桶的位置)。
reHashSize:该容器可以扩容。需要一个桶的大小值。
hashSize:通过一个值,提供一个等于该值或者大于该值的值。(提供合适的值,提供素数是不错的办法)

供外部使用的方法:

  HashMap()
    :_calcHash(CalcHash()), _listValues(), _nBucketSize(0), _arrBuckets(nullptr)
  {
    reHashSize(hashSize(1));
  }

  HashMap(const CalcHash& func)
    :_calcHash(func), _listValues(), _nBucketSize(0), _arrBuckets(nullptr) 
  {
    reHashSize(hashSize(1));
  }

  HashMap(const HashMap<Key, Value, CalcHash>& hashMap)
    :_calcHash(hashMap._calcHash), _listValues(hashMap._listValues), _nBucketSize(0), _arrBuckets(nullptr)
  {
    reHashSize(hashSize(_listValues.size()));
  }

  HashMap(const initializer_list<ValueType>& list, const CalcHash& func = CalcHash())
    :_calcHash(func), _listValues(list), _nBucketSize(0), _arrBuckets(nullptr)
  {
    reHashSize(hashSize(_listValues.size()));
  }

  ~HashMap()
  {
    if (_arrBuckets)
      delete[] _arrBuckets;
  }

  std::size_t BucketSize()
  {
    return _nBucketSize;
  }
    iterator begin()
  {
    return _listValues.begin();
  }

  iterator end()
  {
    return _listValues.end();
  }

  const_iterator cbegin() const
  {
    return _listValues.begin();
  }

  const_iterator cend() const
  {
    return _listValues.end();
  }

  bool empty()
  {
    return _listValues.empty();
  }

  Value& at(const Key& k)
  {
    if ((_listValues.size()) >= (_nBucketSize << 1))
      reHashSize(hashSize(_listValues.size()));
    return at(k, calcHashCode(k));
  }

  HashMap<Key, Value, CalcHash>& operator<<(const ValueType& v)
  {
    at(v.first) = v.second;
    return *this;
  }

  Value& operator[](const Key& k)
  {
    return at(k);
  }

  iterator find(const Key& k)
  {
    return find(k, calcHashCode(k));
  }

  void erase(iterator it)
  {
    erase(it, calcHashCode(it->first));
  }

  void erase(const Key& k)
  {
    std::size_t nHashCode = calcHashCode(k);
    erase(find(k, nHashCode), nHashCode);

  }

  pair<iterator, bool> insert(const ValueType& v)
  {
    if ((_listValues.size()) >= (_nBucketSize << 1))
      reHashSize(hashSize(_listValues.size()));
    return insert(v, calcHashCode(v.first));
  }

  void clear()
  {
    _listValues.clear();
    iterator end = _listValues.end();
    for (size_t i = 0; i != num_buckets_; ++i)
      buckets_[i].first = buckets_[i].last = end;
  }

大家,都自己实现自己的HashMap吧!

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值