大家看这篇博客前,需要先知道哈希表的原理,这篇博客只是对哈希表进行封装。如果没有看过的可以看看我写的这篇博客:C++哈希表。同时读者需要知道STL的unordered_map/set的用法
cpp
#pragma once
#include<iostream>
#include<vector>
#include<list>
#include<string>
#include<variant>
#include<map>
#include<set>
#include<type_traits>
namespace dgj {
template<class T>
struct hash {
size_t operator()(const T& x)const {
return x;
}
};
template<>
struct hash<std::string> {
size_t operator()(const std::string& str)const{
size_t pos = 0;
for (auto ch : str) {
pos = pos * 131 + ch;
}
return pos;
}
};
template<class ...Args>
struct overloads:public Args... {
using Args::operator() ...;
};
template<class ...Args>
overloads(Args...)->overloads<Args...>;
template<class T,class hashfunc=hash<T>>
class Hash {
using self = Hash<T, hashfunc>;
//哈希映射
size_t hashMap(size_t hashNum, size_t capacity)const{
if (!capacity)return 0;
//如果是2的幂就走优化
if (!(capacity & (capacity - 1))) {
return hashNum & (capacity - 1);
}
else {
return hashNum % capacity;
}
}
size_t findNextPowerOfTwo(size_t n) {
if (n == 0) {
return 1;
}
// 位运算技巧:将n的最高位之后的位都置1,然后加1
n--;
n |= n >> 1;
n |= n >> 2;
n |= n >> 4;
n |= n >> 8;
n |= n >> 16;
// 64位系统需补充n |= n >> 32;
if constexpr (sizeof(int*) == 8) {
n |= n >> 32;
}
return n + 1;
}
public:
Hash() {
_table.resize(_buckets);
}
void insert(const T& x) {
size_t pos = hashMap(_hash(x), _buckets);
/*std::visit(Vist(x, pos,_table), _table[pos]);*/
std::visit(overloads(
[&](std::list<T>& ls) {
ls.emplace_back(x);
if (ls.size() >= 8) {
std::set<T>st(ls.begin(), ls.end());
_table[pos] = std::move(st);
}
},
[&](std::set<T>& st) {
st.emplace(x);
}
), _table[pos]);
++_size;
if (load_factor() > _max_load_factor) {
reserve(_size + 1); // 扩容到能容纳_size+1个元素的大小
}
}
bool find(const T& x)const {
size_t pos = hashMap(_hash(x), _buckets);
return std::visit(overloads{
[&](const std::list<T>& ls) {return std::find(ls.begin(), ls.end(), x) != ls.end(); },
[&](const std::set<T>& st) {return static_cast<bool>(st.count(x)); }
}
,_table[pos]
);
}
bool erase(const T& x) {
size_t pos = hashMap(_hash(x), _buckets);
return std::visit(overloads{
[&](std::list<T>& num) {
auto it = std::find(num.begin(), num.end(), x);
if (it == num.end())return false;
num.erase(it);
_size--;
return true;
},
[&](std::set<T>& num) {
auto it = num.find(x);
if (it == num.end())return false;
num.erase(it);
_size--;
return true;
}
}, _table[pos]);
}
void swap(self& x) {
std::swap(x._size, _size);
std::swap(x._buckets, _buckets);
std::swap(x._table, _table);
std::swap(x._hash, _hash);
std::swap(_max_load_factor, x._max_load_factor);
}
public:
float max_load_factor()const {
return _max_load_factor;
}
void max_load_factor(float factor) {
_max_load_factor = factor;
}
float load_factor()const {
return _size / (1.0f * _buckets);
}
void reserve(size_t size) {
size_t reSize = findNextPowerOfTwo(size / _max_load_factor);
rehash(reSize);
}
void rehash(size_t size) {
size_t reSize = findNextPowerOfTwo(size);
if (reSize <= _buckets)return;
self newHash;
newHash._buckets = reSize;
newHash._max_load_factor = _max_load_factor;
newHash._table.resize(reSize);
for (auto& num : _table) {
std::visit(overloads{
[&](auto&& k) {
for (auto n : k) {
newHash.insert(n);
}
}
}, num);
}
swap(newHash);
}
void clear() {
_size = 0;
_buckets = _default_bucket;
_table.resize(_default_bucket);
_max_load_factor = _default_factor;
for (auto& num : _table) {
std::visit(overloads{
[](auto&& k) {
k.clear();
}
}, num);
}
}
public:
size_t size()const {
return _size;
}
size_t bucket_count() {
return _buckets;
}
size_t bucket_size(size_t n) {
if (n >= _buckets)return -1;
return std::visit(overloads([](auto&& num) {return num.size(); }), _table[n]);
}
private:
inline const static size_t _default_bucket = 8;
inline const static float _default_factor = 1.0;
float _max_load_factor = _default_factor;
size_t _size = 0;
size_t _buckets = _default_bucket;
std::vector < std::variant<std::list<T>,std::set<T>> > _table;
hashfunc _hash;
};
}
这是在C++哈希表博客里面最后实现的代码,他已经可以作为哈希表使用了,只是需要封装接口,加上迭代器。但是这个出现第一个问题
模板统一K-V和K存储
问题一
我们上面只是实现了存储K的哈希表,那么我们该如何实现一个存储K-V的哈希表呢?很简单,模板加上K-V,把插入换成K-V,然后最终存V就行了。但是和K的哈希表高度相似,这太不优雅了。
我们发现查找K-V或者是K的哈希表都是通过K来查找的,只是最后存储的不同,一个存储K一个存储V。这里我们也可以理解K哈希表可以看成K-K的哈希表,即K-V的特殊情况。
但是K-V只存V是否可以呢?查找的时候,我们是用K去比较的,但是底层只存了,V,那么K不知道和谁比了,因此要存储K-V结构。
问题二
我们查找删除等操作,用K去找,和最后的V怎么比较?因此我们需要独立出来一个比较函数,对于K的哈希表我们直接返回K,对于K-V的哈希表我们要返回第一个K。
这里我们传入一个GetK的仿函数来区分处理
问题三
set怎么存储V?,到时候查找的时候只要比较K就行,因此还需要给set传入一个compare的仿函数
这里还得传GetK的仿函数,不然不知道怎么获取K
问题四
我怎么拿K去找set<V>,创建一个V,V的first给K,second给个默认值即可,然后传进去就行了
实现
cpp
#pragma once
#include<iostream>
#include<vector>
#include<list>
#include<string>
#include<variant>
#include<map>
#include<set>
#include<type_traits>
namespace dgj {
template<class K>
struct hash {
size_t operator()(const K& x)const {
return x;
}
};
template<>
struct hash<std::string> {
size_t operator()(const std::string& str)const{
size_t pos = 0;
for (auto ch : str) {
pos = pos * 131 + ch;
}
return pos;
}
};
template<class ...Args>
struct overloads:public Args... {
using Args::operator() ...;
};
template<class ...Args>
overloads(Args...)->overloads<Args...>;
template<class K,class V,class GetK, class hashfunc = hash<K>>
class Hash {
using self = Hash<K,V, GetK,hashfunc>;
size_t hashMap(size_t hashNum, size_t capacity)const{
if (!capacity)return 0;
if (!(capacity & (capacity - 1))) {
return hashNum & (capacity - 1);
}
else {
return hashNum % capacity;
}
}
size_t findNextPowerOfTwo(size_t n) {
if (n == 0) {
return 1;
}
n--;
n |= n >> 1;
n |= n >> 2;
n |= n >> 4;
n |= n >> 8;
n |= n >> 16;
if constexpr (sizeof(int*) == 8) {
n |= n >> 32;
}
return n + 1;
}
public:
Hash() {
_table.resize(_buckets);
}
void insert(const K& key,const V&value) {
size_t pos = hashMap(_hash(key), _buckets);
std::visit(overloads(
[&](std::list<V>& ls) {
ls.emplace_back(value);
if (ls.size() >= 8) {
std::set< V, _setcompare<V, GetK>>st(ls.begin(), ls.end());
_table[pos] = std::move(st);
}
},
[&](std::set< V, _setcompare<V, GetK>>& st) {
st.emplace(value);
}
), _table[pos]);
++_size;
if (load_factor() > _max_load_factor) {
reserve(_size + 1);
}
}
bool find(const K& key)const {
size_t pos = hashMap(_hash(key), _buckets);
return std::visit(overloads{
[&](const std::list<V>& ls) {
for (const V& v : ls) {
if (_getk(v) == key) {
return true;
}
}
return false;
},
[&](const std::set< V, _setcompare<V, GetK>>& st) {return static_cast<bool>(st.count({key,V().second})); }
}
,_table[pos]
);
}
bool erase(const K& key) {
size_t pos = hashMap(_hash(key), _buckets);
return std::visit(overloads{
[&](std::list<V>& num) {
for (auto it = num.begin(); it != num.end(); ++it) {
if (_getk(*it) == key) {
num.erase(it);
_size--;
return true;
}
}
return false;
},
[&](std::set< V, _setcompare<V, GetK>>& num) {
V pr = {key,V().second};
auto it = num.find(pr);
if (it == num.end())return false;
num.erase(it);
_size--;
return true;
}
}, _table[pos]);
}
void swap(self& x) {
if (&x == this)return;
std::swap(x._size, _size);
std::swap(x._buckets, _buckets);
std::swap(x._table, _table);
std::swap(x._hash, _hash);
std::swap(_max_load_factor, x._max_load_factor);
std::swap(x._getk, _getk);
}
public:
float max_load_factor()const {
return _max_load_factor;
}
void max_load_factor(float factor) {
_max_load_factor = factor;
}
float load_factor()const {
return _size / (1.0f * _buckets);
}
void reserve(size_t size) {
size_t reSize = findNextPowerOfTwo((size*1.0)/ _max_load_factor);
rehash(reSize);
}
void rehash(size_t size) {
size_t reSize = findNextPowerOfTwo(size);
if (reSize <= _buckets)return;
self newHash;
newHash._buckets = reSize;
newHash._max_load_factor = _max_load_factor;
newHash._table.resize(reSize);
for (auto& num : _table) {
std::visit(overloads{
[&](auto&& k) {
for (auto n : k) {
newHash.insert(_getk(n), n);
}
}
}, num);
}
swap(newHash);
}
void clear() {
_size = 0;
_buckets = _default_bucket;
_table.resize(_default_bucket);
_max_load_factor = _default_factor;
for (auto& num : _table) {
std::visit(overloads{
[](auto&& k) {
k.clear();
}
}, num);
}
}
public:
size_t size()const {
return _size;
}
size_t bucket_count() {
return _buckets;
}
size_t bucket_size(size_t n) {
if (n >= _buckets)return -1;
return std::visit(overloads([](auto&& num) {return num.size(); }), _table[n]);
}
private:
template<class V,class GetK>
struct _setcompare {
bool operator()(const V& x, const V& y)const {
return _getk(x) < _getk(y);
};
GetK _getk;
};
//增加获取k的仿函数
GetK _getk;
inline const static size_t _default_bucket = 8;
inline const static float _default_factor = 1.0;
float _max_load_factor = _default_factor;
size_t _size = 0;
size_t _buckets = _default_bucket;
std::vector < std::variant < std::list<V>, std::set < V,_setcompare<V, GetK> >> > _table;
hashfunc _hash;
};
}
问题五

Pred模板参数问题,Pred是为了传operator==的,但是哈希表底层会转set,set又是用operator<的,那岂不冲突了吗?这里说明一下unordered_map/set底层并不会转set,因此不传operator<。所以我们可以删除set的转换
实现
删除set那么就简单多了
cpp
#pragma once
#include<iostream>
#include<vector>
#include<list>
#include<string>
#include<variant>
#include<map>
#include<set>
#include<type_traits>
namespace dgj {
template<class K>
struct hashfunc {
size_t operator()(const K& x)const {
return x;
}
};
template<>
struct hashfunc<std::string> {
size_t operator()(const std::string& str)const {
size_t pos = 0;
for (auto ch : str) {
pos = pos * 131 + ch;
}
return pos;
}
};
template<class K>
struct predfunc {
bool operator()(const K& x, const K& y) {
return x == y;
}
};
}
namespace dgj {
template<class K,class V,class GetK, class hashfunc,class predfunc>
class Hash {
using self = Hash<K,V, GetK,hashfunc, predfunc>;
size_t hashMap(size_t hashNum, size_t capacity)const{
if (!capacity)return 0;
if (!(capacity & (capacity - 1))) {
return hashNum & (capacity - 1);
}
else {
return hashNum % capacity;
}
}
size_t findNextPowerOfTwo(size_t n) {
if (n == 0) {
return 1;
}
n--;
n |= n >> 1;
n |= n >> 2;
n |= n >> 4;
n |= n >> 8;
n |= n >> 16;
if constexpr (sizeof(int*) == 8) {
n |= n >> 32;
}
return n + 1;
}
public:
Hash() {
_table.resize(_buckets);
}
void insert(const K& key,const V&value) {
size_t pos = hashMap(_hash(key), _buckets);
if (_table[pos].end() != std::find_if(_table[pos].begin(), _table[pos].end(), [&](const V& v) {return _getk(v) == key; })) {
return;
}
_table[pos].emplace_back(value);
++_size;
if (load_factor() > _max_load_factor) {
reserve(_size + 1);
}
}
template<class...Args >
void emplace(Args&&...args) {
std::tuple<Args&&...>tp = std::forward_as_tuple(std::forward<Args>(args)...);
const K& key = std::get<0>(tp);
size_t pos = hashMap(_hash(key), _buckets);
if (_table[pos].end() != std::find_if(_table[pos].begin(), _table[pos].end(), [&](const V& v) {return _getk(v) == key; }))return;
if constexpr (sizeof...(Args) == 2) {
// 场景1:参数是键+值(各1个参数),直接原位构造
_table[pos].emplace_back(
std::forward<decltype(std::get<0>(tp))>(std::get<0>(tp)),
std::forward<decltype(std::get<1>(tp))>(std::get<1>(tp))
);
}
else {
// 场景2:参数是键+值的多参数(比如emplace(1, 5, 'a')),需分段构造(后续优化)
// 这里先给出错误提示,或扩展为通用分段构造逻辑
static_assert(sizeof...(Args) == 2, "仅支持键+值各一个参数的场景,多参数请使用分段构造");
}
if (load_factor() > _max_load_factor) {
reserve(_size + 1);
}
}
bool find(const K& key)const {
size_t pos = hashMap(_hash(key), _buckets);
return _table[pos].end()!=std::find_if(_table[pos].begin(), _table[pos].end(), [&](const V& v) {return _getk(v) == key; });
}
size_t count(const K& key)const {
if (find(key))return 1;
else return 0;
}
bool erase(const K& key) {
size_t pos = hashMap(_hash(key), _buckets);
auto it = std::find_if(_table[pos].begin(), _table[pos].end(), [&](const V& v) {return _getk(v) == key; });
if (it != _table[pos].end()) {
_table[pos].erase(it);
--_size;
return true;
}
return false;
}
void swap(self& x) {
if (&x == this)return;
std::swap(x._size, _size);
std::swap(x._buckets, _buckets);
std::swap(x._table, _table);
std::swap(x._hash, _hash);
std::swap(_max_load_factor, x._max_load_factor);
std::swap(x._getk, _getk);
}
public:
float max_load_factor()const {
return _max_load_factor;
}
void max_load_factor(float factor) {
_max_load_factor = factor;
}
float load_factor()const {
return _size / (1.0f * _buckets);
}
void reserve(size_t size) {
size_t reSize = findNextPowerOfTwo((size*1.0)/ _max_load_factor);
rehash(reSize);
}
void rehash(size_t size) {
size_t reSize = findNextPowerOfTwo(size);
if (reSize <= _buckets)return;
self newHash;
newHash._buckets = reSize;
newHash._max_load_factor = _max_load_factor;
newHash._table.resize(reSize);
for (auto& num : _table) {
for (auto n : num) {
newHash.insert(_getk(n), n);
}
}
swap(newHash);
}
void clear() {
_size = 0;
_buckets = _default_bucket;
_table.resize(_default_bucket);
_max_load_factor = _default_factor;
for (auto& num : _table) {
num.clear();
}
}
public:
size_t size()const {
return _size;
}
bool empty()const {
return !_size;
}
size_t bucket_count()const {
return _buckets;
}
size_t max_bucket_count()const {
return _table.size();
}
size_t bucket_size(size_t n)const {
if (n >= _buckets)return -1;
return _table[n].size();
}
private:
//增加获取k的仿函数
GetK _getk;
inline const static size_t _default_bucket = 8;
inline const static float _default_factor = 1.0;
float _max_load_factor = _default_factor;
size_t _size = 0;
size_t _buckets = _default_bucket;
std::vector <std::list<V>> _table;
hashfunc _hash;
};
}
顺便加了count,empty,emplace
实现unordered_map/set
那还说啥了,直接套hash类就行了
cpp
namespace dgj {
template<class K,class T, class HashFunc = hashfunc<K>, class PredFunc = predfunc<K>>
class unorderedMap {
struct GetK {
const K& operator()(const T& t) {
return t.first;
}
};
using self = unorderedMap<K,T,HashFunc, PredFunc>;
public:
bool empty()const { return _hash.empty(); }
size_t size()const { return _hash.size(); }
size_t count(const K& k) { return _hash.count(k); }
template<class...Args>
void emplace(Args&& args) { _hash.emplace(std::forward<Args>(args)...); }
void insert(const K& k,const K&v) { _hash.insert(k, v); }
void erase(const K& k) { _hash.erase(k); }
void clear() { _hash.clear(); }
void swap(self& x) { _hash.swap(x._hash); }
size_t bucket_count()const { return _hash.bucket_count(); }
size_t max_bucket_count()const { return _hash.max_bucket_count(); }
size_t bucket_size(size_t pos)const { return _hash.bucket_size(pos); }
float load_factor()const { return _hash.load_factor(); }
void max_load_factor(float f) { return _hash.max_load_factor(f); }
float max_load_factor()const { return _hash.max_load_factor(); }
void rehash(size_t size) { return _hash.rehash(size); }
void reserve(size_t size) { return _hash.reserve(size); }
private:
Hash<K, T, GetK, HashFunc, PredFunc>_hash;
};
}
namespace dgj {
template<class K,class HashFunc=hashfunc<K>,class PredFunc=predfunc<K>>
class unorderedSet{
template<class K>
struct getk(const V& v) {
return v;
}
using self = unorderedSet<K, HashFunc, PredFunc>;
public:
bool empty()const {return _hash.empty();}
size_t size()const {return _hash.size();}
size_t count(const K& k) {return _hash.count(k);}
template<class...Args>
void emplace(Args&& args) {_hash.emplace(std::forward<Args>(args)...);}
void insert(const K& k) {_hash.insert(k, k);}
void erase(const K&k) {_hash.erase(k);}
void clear() {_hash.clear();}
void swap(self&x) {_hash.swap(x._hash);}
size_t bucket_count()const {return _hash.bucket_count();}
size_t max_bucket_count()const {return _hash.max_bucket_count();}
size_t bucket_size(size_t pos)const {return _hash.bucket_size(pos);}
float load_factor()const {return _hash.load_factor();}
void max_load_factor(float f) {return _hash.max_load_factor(f);}
float max_load_factor()const {return _hash.max_load_factor();}
void rehash(size_t size) {return _hash.rehash(size);}
void reserve(size_t size) {return _hash.reserve(size);}
private:
Hash<K, K, getk, HashFunc, PredFunc>_hash;
};
}
迭代器实现
底层主要存储对应桶的指针,然后是桶下标和当前桶的迭代器(这里用的是list的迭代器)。那么就可以实现了。
++就是先走底层list迭代器,如果到末尾了,跳到下一个非空的桶里。
==先比较是否是一个桶再比较是否是一个list迭代器
cpp
template<class K,class V,class Ptr,class Ref>
class HashIterator {
using Self = HashIterator<K, V, Ptr, Ref>;
using ReSelf = Self&;
using PtrSelf = Self*;
public:
HashIterator(const Self& s)
:_bucket_pos(s._bucket_pos),
_it(s._it),
_table(s._table)
{
}
HashIterator(size_t pos, std::list<V>::iterator it, std::vector <std::list<V>>* table)
:_bucket_pos(pos),
_it(it),
_table(table)
{
}
Ref operator*() {
return *_it;
}
Ptr operator->() {
return &(*_it);
}
ReSelf operator=(Self& x) {
if (this != &x) {
_bucket_pos = x._bucket_pos;
_it = x._it;
_table = x._table;
}
return *this;
}
ReSelf operator++() {
++_it;
while (_it == (*_table)[_bucket_pos].end()) {
_it = (*_table)[++_bucket_pos].begin();
if (_bucket_pos==_table->size()-1&&_it==_table->back().end())break;
}
return *this;
}
bool operator ==(const Self&it)const {
return _table == it._table && _bucket_pos == it._bucket_pos && _it == it._it;
}
HashIterator operator++(int) {
HashIterator ret = *this;
++(*this);
return ret;
}
private:
size_t _bucket_pos = 0;
std::list<V>::iterator _it;//这里是iterator ,如果是const_iterator传就构造不了了。应该是这里的问题,
std::vector <std::list<V>>* _table;
};
template<class K,class V,class GetK, class hashfunc,class predfunc>
class Hash {
using self = Hash<K,V, GetK,hashfunc, predfunc>;
using Iterator = HashIterator<const K, V,V*,V&>;
using const_Iterator = HashIterator<const K,V,const V*,const V&>;
size_t hashMap(size_t hashNum, size_t capacity)const{
if (!capacity)return 0;
if (!(capacity & (capacity - 1))) {
return hashNum & (capacity - 1);
}
else {
return hashNum % capacity;
}
}
size_t findNextPowerOfTwo(size_t n) {
if (n == 0) {
return 1;
}
n--;
n |= n >> 1;
n |= n >> 2;
n |= n >> 4;
n |= n >> 8;
n |= n >> 16;
if constexpr (sizeof(int*) == 8) {
n |= n >> 32;
}
return n + 1;
}
public:
Iterator begin() {
size_t pos = 0;
while (pos<_buckets&&_table[pos].empty())++pos;
if (pos == _buckets)return end();
return { pos,_table[pos].begin(),&_table };
}
Iterator end() {
return { _buckets-1,_table.back().end(),&_table};
}
const_Iterator begin()const {
size_t pos = 0;
while (pos < _buckets && _table[pos].empty())++pos;
if (pos == _buckets)return end();
return const_Iterator(pos,_table[pos].begin(),&_table );
}
const_Iterator end()const{
return const_Iterator(_buckets - 1,_table.back().end(),&_table);
}
public:
Hash() {
_table.resize(_buckets);
}
std::pair<Iterator,bool> insert(const K& key,const V&value) {
size_t pos = hashMap(_hash(key), _buckets);
auto it = std::find_if(_table[pos].begin(), _table[pos].end(), [&](const V& v) {return _getk(v) == key; });
if (_table[pos].end() != it) {
return { {pos,it,&_table},false };
}
_table[pos].emplace_back(value);
++_size;
if (load_factor() > _max_load_factor) {
reserve(_size + 1);
return { find(key),true };
}
else {
return { {pos,--_table[pos].end(),&_table},true };
}
}
std::pair<Iterator, bool> insert(const K& key,V&& value) {
size_t pos = hashMap(_hash(key), _buckets);
auto it = std::find_if(_table[pos].begin(), _table[pos].end(), [&](const V& v) {return _getk(v) == key; });
if (_table[pos].end() != it) {
return { {pos,it,&_table},false };
}
_table[pos].emplace_back(std::forward<V>(value));
++_size;
if (load_factor() > _max_load_factor) {
reserve(_size + 1);
return { find(key),true };
}
else {
return { {pos,--_table[pos].end(),&_table},true };
}
}
template<class...Args >
std::pair<Iterator, bool> emplace(Args&&...args) {
std::tuple<Args&&...>tp = std::forward_as_tuple(std::forward<Args>(args)...);
const K& key = std::get<0>(tp);
size_t pos = hashMap(_hash(key), _buckets);
auto it = std::find_if(_table[pos].begin(), _table[pos].end(), [&](const V& v) {return _getk(v) == key; });
if (_table[pos].end() != it) {
return { {pos,it,&_table},false };
}
if constexpr (sizeof...(Args) == 2) {
// 场景1:参数是键+值(各1个参数),直接原位构造
_table[pos].emplace_back(
std::forward<decltype(std::get<0>(tp))>(std::get<0>(tp)),
std::forward<decltype(std::get<1>(tp))>(std::get<1>(tp))
);
}
else {
// 场景2:参数是键+值的多参数(比如emplace(1, 5, 'a')),需分段构造(后续优化)
// 这里先给出错误提示,或扩展为通用分段构造逻辑
static_assert(sizeof...(Args) == 2, "仅支持键+值各一个参数的场景,多参数请使用分段构造");
}
if (load_factor() > _max_load_factor) {
reserve(_size + 1);
return { find(key),true };
}
else {
return { {pos,--_table[pos].end(),&_table},true };
}
}
const_Iterator find(const K& key)const {
size_t pos = hashMap(_hash(key), _buckets);
auto it = std::find_if(_table[pos].begin(), _table[pos].end(), [&](const V& v) {return _getk(v) == key; });
if (it != _table[pos].end())return { pos,it,&_table };
else return end();
}
Iterator find(const K& key){
size_t pos = hashMap(_hash(key), _buckets);
auto it = std::find_if(_table[pos].begin(), _table[pos].end(), [&](const V& v) {return _getk(v) == key; });
if (it != _table[pos].end())return { pos,it,&_table };
else return end();
}
size_t count(const K& key)const {
if (find(key))return 1;
else return 0;
}
bool erase(const K& key) {
size_t pos = hashMap(_hash(key), _buckets);
auto it = std::find_if(_table[pos].begin(), _table[pos].end(), [&](const V& v) {return _getk(v) == key; });
if (it != _table[pos].end()) {
_table[pos].erase(it);
--_size;
return true;
}
return false;
}
void swap(self& x) {
if (&x == this)return;
std::swap(x._size, _size);
std::swap(x._buckets, _buckets);
std::swap(x._table, _table);
std::swap(x._hash, _hash);
std::swap(_max_load_factor, x._max_load_factor);
std::swap(x._getk, _getk);
}
public:
float max_load_factor()const {
return _max_load_factor;
}
void max_load_factor(float factor) {
_max_load_factor = factor;
}
float load_factor()const {
return _size / (1.0f * _buckets);
}
void reserve(size_t size) {
size_t reSize = findNextPowerOfTwo((size*1.0)/ _max_load_factor);
rehash(reSize);
}
void rehash(size_t size) {
size_t reSize = findNextPowerOfTwo(size);
if (reSize <= _buckets)return;
self newHash;
newHash._buckets = reSize;
newHash._max_load_factor = _max_load_factor;
newHash._table.resize(reSize);
for (auto& num : _table) {
for (auto n : num) {
newHash.insert(_getk(n), n);
}
}
swap(newHash);
}
void clear() {
_size = 0;
_buckets = _default_bucket;
_table.resize(_default_bucket);
_max_load_factor = _default_factor;
for (auto& num : _table) {
num.clear();
}
}
public:
size_t size()const {
return _size;
}
bool empty()const {
return !_size;
}
size_t bucket_count()const {
return _buckets;
}
size_t max_bucket_count()const {
return _table.size();
}
size_t bucket_size(size_t n)const {
if (n >= _buckets)return -1;
return _table[n].size();
}
private:
//增加获取k的仿函数
GetK _getk;
inline const static size_t _default_bucket = 8;
inline const static float _default_factor = 1.0;
float _max_load_factor = _default_factor;
size_t _size = 0;
size_t _buckets = _default_bucket;
std::vector <std::list<V>> _table;
hashfunc _hash;
};
但是上面的实现是有点小问题的,如果我们构造一个const unordered_map那么底层的Hash就是const的,那么list就会返回const_iterator。但是我们的迭代器不支持用const_iterator构造,因此就有问题:所以我们加一个const的迭代器实现
cpp
#pragma once
#include<iostream>
#include<vector>
#include<list>
#include<string>
#include<variant>
#include<map>
#include<set>
#include<type_traits>
namespace dgj {
template<class K>
struct hashfunc {
size_t operator()(const K& x)const {
return x;
}
};
template<>
struct hashfunc<std::string> {
size_t operator()(const std::string& str)const {
size_t pos = 0;
for (auto ch : str) {
pos = pos * 131 + ch;
}
return pos;
}
};
template<class K>
struct predfunc {
bool operator()(const K& x, const K& y) {
return x == y;
}
};
}
namespace dgj {
template<class K,class V,class Ptr,class Ref>
class HashIterator {
using Self = HashIterator<K, V, Ptr, Ref>;
using ReSelf = Self&;
using PtrSelf = Self*;
public:
HashIterator(const Self& s)
:_bucket_pos(s._bucket_pos),
_it(s._it),
_table(s._table)
{
}
HashIterator(size_t pos, std::list<V>::iterator it, std::vector <std::list<V>>* table)
:_bucket_pos(pos),
_it(it),
_table(table)
{
}
Ref operator*() {
return *_it;
}
Ptr operator->() {
return &(*_it);
}
ReSelf operator=(Self& x) {
if (this != &x) {
_bucket_pos = x._bucket_pos;
_it = x._it;
_table = x._table;
}
return *this;
}
ReSelf operator++() {
++_it;
while (_it == (*_table)[_bucket_pos].end()) {
_it = (*_table)[++_bucket_pos].begin();
if (_bucket_pos == _table->size() - 1 && _it == _table->back().end())break;
}
return *this;
}
bool operator ==(const Self& mit)const {
return _table == mit._table && _bucket_pos == mit._bucket_pos && mit._it == _it;
}
HashIterator operator++(int) {
HashIterator ret = *this;
++(*this);
return ret;
}
private:
size_t _bucket_pos = 0;
std::list<V>::iterator _it;//这里是iterator ,如果是const_iterator传就构造不了了。应该是这里的问题,
std::vector <std::list<V>>* _table;
};
template<class K, class V, class Ptr, class Ref>
class constHashIterator {
using Self = constHashIterator<K, V, Ptr, Ref>;
using ReSelf = Self&;
using PtrSelf = Self*;
public:
constHashIterator(const Self& s)
:_bucket_pos(s._bucket_pos),
_it(s._it),
_table(s._table)
{
}
constHashIterator(size_t pos, std::list<V>::const_iterator it,const std::vector <std::list<V>>* table)
:_bucket_pos(pos),
_it(it),
_table(table)
{
}
Ref operator*() {
return *_it;
}
Ptr operator->() {
return &(*_it);
}
ReSelf operator=(Self& x) {
if (this != &x) {
_bucket_pos = x._bucket_pos;
_it = x._it;
_table = x._table;
}
return *this;
}
ReSelf operator++() {
++_it;
while (_it == (*_table)[_bucket_pos].end()) {
_it = (*_table)[++_bucket_pos].begin();
if (_bucket_pos == _table->size() - 1 && _it == _table->back().end())break;
}
return *this;
}
bool operator ==(const Self& mit)const {
return _table == mit._table && _bucket_pos == mit._bucket_pos && mit._it == _it;
}
constHashIterator operator++(int) {
constHashIterator ret = *this;
++(*this);
return ret;
}
private:
size_t _bucket_pos = 0;
std::list<V>::const_iterator _it;//这里是iterator ,如果是const_iterator传就构造不了了。应该是这里的问题,
const std::vector <std::list<V>>* _table;
};
template<class K,class V,class GetK, class hashfunc,class predfunc>
class Hash {
using self = Hash<K,V, GetK,hashfunc, predfunc>;
using Iterator = HashIterator< K, V,V*,V&>;
using const_Iterator = constHashIterator< K,V,const V*,const V&>;
size_t hashMap(size_t hashNum, size_t capacity)const{
if (!capacity)return 0;
if (!(capacity & (capacity - 1))) {
return hashNum & (capacity - 1);
}
else {
return hashNum % capacity;
}
}
size_t findNextPowerOfTwo(size_t n) {
if (n == 0) {
return 1;
}
n--;
n |= n >> 1;
n |= n >> 2;
n |= n >> 4;
n |= n >> 8;
n |= n >> 16;
if constexpr (sizeof(int*) == 8) {
n |= n >> 32;
}
return n + 1;
}
public:
Iterator begin() {
size_t pos = 0;
while (pos<_buckets&&_table[pos].empty())++pos;
if (pos == _buckets)return end();
return { pos,_table[pos].begin(),&_table };
}
Iterator end() {
return { _buckets-1,_table.back().end(),&_table};
}
const_Iterator begin()const {
size_t pos = 0;
while (pos < _buckets && _table[pos].empty())++pos;
if (pos == _buckets)return end();
return const_Iterator(pos,_table[pos].begin(),&_table );
}
const_Iterator end()const{
return const_Iterator(_buckets - 1,_table.back().end(),&_table);
}
public:
Hash() {
_table.resize(_buckets);
}
std::pair<Iterator,bool> insert(const K& key,const V&value) {
size_t pos = hashMap(_hash(key), _buckets);
auto it = std::find_if(_table[pos].begin(), _table[pos].end(), [&](const V& v) {return _getk(v) == key; });
if (_table[pos].end() != it) {
return { {pos,it,&_table},false };
}
_table[pos].emplace_back(value);
++_size;
if (load_factor() > _max_load_factor) {
reserve(_size + 1);
return { find(key),true };
}
else {
return { {pos,--_table[pos].end(),&_table},true };
}
}
std::pair<Iterator, bool> insert(const K& key,V&& value) {
size_t pos = hashMap(_hash(key), _buckets);
auto it = std::find_if(_table[pos].begin(), _table[pos].end(), [&](const V& v) {return _getk(v) == key; });
if (_table[pos].end() != it) {
return { {pos,it,&_table},false };
}
_table[pos].emplace_back(std::forward<V>(value));
++_size;
if (load_factor() > _max_load_factor) {
reserve(_size + 1);
return { find(key),true };
}
else {
return { {pos,--_table[pos].end(),&_table},true };
}
}
template<class...Args >
std::pair<Iterator, bool> emplace(Args&&...args) {
std::tuple<Args&&...>tp = std::forward_as_tuple(std::forward<Args>(args)...);
const K& key = std::get<0>(tp);
size_t pos = hashMap(_hash(key), _buckets);
auto it = std::find_if(_table[pos].begin(), _table[pos].end(), [&](const V& v) {return _getk(v) == key; });
if (_table[pos].end() != it) {
return { {pos,it,&_table},false };
}
if constexpr (sizeof...(Args) == 2) {
// 场景1:参数是键+值(各1个参数),直接原位构造
_table[pos].emplace_back(
std::forward<decltype(std::get<0>(tp))>(std::get<0>(tp)),
std::forward<decltype(std::get<1>(tp))>(std::get<1>(tp))
);
}
else {
// 场景2:参数是键+值的多参数(比如emplace(1, 5, 'a')),需分段构造(后续优化)
// 这里先给出错误提示,或扩展为通用分段构造逻辑
static_assert(sizeof...(Args) == 2, "仅支持键+值各一个参数的场景,多参数请使用分段构造");
}
if (load_factor() > _max_load_factor) {
reserve(_size + 1);
return { find(key),true };
}
else {
return { {pos,--_table[pos].end(),&_table},true };
}
}
const_Iterator find(const K& key)const {
size_t pos = hashMap(_hash(key), _buckets);
auto it = std::find_if(_table[pos].begin(), _table[pos].end(), [&](const V& v) {return _getk(v) == key; });
if (it != _table[pos].end())return { pos,it,&_table };
else return end();
}
Iterator find(const K& key){
size_t pos = hashMap(_hash(key), _buckets);
auto it = std::find_if(_table[pos].begin(), _table[pos].end(), [&](const V& v) {return _getk(v) == key; });
if (it != _table[pos].end())return { pos,it,&_table };
else return end();
}
size_t count(const K& key)const {
if (find(key))return 1;
else return 0;
}
bool erase(const K& key) {
size_t pos = hashMap(_hash(key), _buckets);
auto it = std::find_if(_table[pos].begin(), _table[pos].end(), [&](const V& v) {return _getk(v) == key; });
if (it != _table[pos].end()) {
_table[pos].erase(it);
--_size;
return true;
}
return false;
}
void swap(self& x) {
if (&x == this)return;
std::swap(x._size, _size);
std::swap(x._buckets, _buckets);
std::swap(x._table, _table);
std::swap(x._hash, _hash);
std::swap(_max_load_factor, x._max_load_factor);
std::swap(x._getk, _getk);
}
public:
float max_load_factor()const {
return _max_load_factor;
}
void max_load_factor(float factor) {
_max_load_factor = factor;
}
float load_factor()const {
return _size / (1.0f * _buckets);
}
void reserve(size_t size) {
size_t reSize = findNextPowerOfTwo((size*1.0)/ _max_load_factor);
rehash(reSize);
}
void rehash(size_t size) {
size_t reSize = findNextPowerOfTwo(size);
if (reSize <= _buckets)return;
self newHash;
newHash._buckets = reSize;
newHash._max_load_factor = _max_load_factor;
newHash._table.resize(reSize);
for (auto& num : _table) {
for (auto n : num) {
newHash.insert(_getk(n), n);
}
}
swap(newHash);
}
void clear() {
_size = 0;
_buckets = _default_bucket;
_table.resize(_default_bucket);
_max_load_factor = _default_factor;
for (auto& num : _table) {
num.clear();
}
}
public:
size_t size()const {
return _size;
}
bool empty()const {
return !_size;
}
size_t bucket_count()const {
return _buckets;
}
size_t max_bucket_count()const {
return _table.size();
}
size_t bucket_size(size_t n)const {
if (n >= _buckets)return -1;
return _table[n].size();
}
private:
//增加获取k的仿函数
GetK _getk;
inline const static size_t _default_bucket = 8;
inline const static float _default_factor = 1.0;
float _max_load_factor = _default_factor;
size_t _size = 0;
size_t _buckets = _default_bucket;
std::vector <std::list<V>> _table;
hashfunc _hash;
};
}
最后加了operator[]的unorderedMap
cpp
#pragma once
#include"hash.h"
#include<string>
#include<initializer_list>
namespace dgj {
template<class K,class T, class HashFunc = hashfunc<K>, class PredFunc = predfunc<K>>
class unorderedMap {
using V = std::pair<K, T>;
using self = unorderedMap<K,T,HashFunc, PredFunc>;
struct GetK {
const K& operator()(const V& t)const {
return t.first;
}
};
public:
using iterator = HashIterator< K, V,V*,V&>;
using const_iterator = constHashIterator< K, V,const V*,const V&>;
public:
iterator begin() { return _hash.begin(); }
iterator end() { return _hash.end(); }
const_iterator begin()const { return _hash.begin(); }
const_iterator end()const const{ return _hash.end(); }
unorderedMap(){}
unorderedMap(const std::initializer_list<V>&il){
size_t size = il.size();
reserve(size);
for (auto& v : il)insert(v);
}
bool empty()const { return _hash.empty(); }
size_t size()const { return _hash.size(); }
size_t count(const K& k) { return _hash.count(k); }
iterator find(const K& k) { return _hash.find(k); }
template<class...Args>
std::pair<iterator,bool> emplace(Args&&... args) { return _hash.emplace(std::forward<Args>(args)...); }
std::pair<iterator, bool> insert(const V&t) { return _hash.insert(t.first, t); }
std::pair<iterator, bool> insert(V&&t) { return _hash.insert(t.first, std::forward<V>(t)); }
void erase(const K& k) { _hash.erase(k); }
void clear() { _hash.clear(); }
void swap(self& x) { _hash.swap(x._hash); }
size_t bucket_count()const { return _hash.bucket_count(); }
size_t max_bucket_count()const { return _hash.max_bucket_count(); }
size_t bucket_size(size_t pos)const { return _hash.bucket_size(pos); }
float load_factor()const { return _hash.load_factor(); }
void max_load_factor(float f) { return _hash.max_load_factor(f); }
float max_load_factor()const { return _hash.max_load_factor(); }
void rehash(size_t size) { return _hash.rehash(size); }
void reserve(size_t size) { return _hash.reserve(size); }
T& operator[](const K& x) {
return emplace(x, T()).first->second;
}
private:
Hash<K, V, GetK, HashFunc, PredFunc>_hash;
};
}