1.hashtable
1.1 概述
hashtable也被成为哈希表(散列表)等、这种结构在插入和删除,以及搜寻等奥做上具有常数平均时间的表现,而且这种表现是以统计为基础,不需要仰赖输入元素的随机性。
hashtable可提供对任何有名项的存取操作和删除操作,由于操作对象是有名项,所以hashtable也可被视为一种字典结构,这种结构的用意在于提供常数时间的基本操作。
我们可以开辟一个足够大的数组,在数组的每一位上,记录我们存入的元素,我们通过散列函数hash function来完成将传入的元素转换为相应位置的功能。例如,当我们传入的信息是一个整数的时候,我们可以将这个整数与我们开辟好的数组长度取余,这样就可以得到最终我们应该将这个数字存放的位置。但是这样子做很容易出现问题,那就是万一两个数字取余后的结果相同,那么就会出现二者在某一位置上发生冲突、因此围殴了解决这种问题,产生了很多种不同的解法:
①线性探测:当我们通过hash function计算出某个元素的插入位置,而这位置上的空间已经不能再使用的时候,我们最简单的方法就是循序往下一一寻找,即直接从下一个位置处开始寻找,只要我们开辟的空间足够大,那么总可以找到一个可以存储的位置,但是相应的时间复杂度就会变高,并且我们可以想象一下,假如某一位置上我们产生了很多次冲突,那么相应的随着元素后移就会占据很多其他元素的空间,这样假如我们之后插入的元素计算出正确的位置,但是由于该位置被别的元素占用,因此也就只能被迫后移,这样的情况发生的越多,最终我们的数组在完成相应的操作的时候也就越复杂。
②二次探测:二次探测运用到的思想与线性探测是类似的,也是通过散列函数hash function来计算处相应的位置,但是在处理哈希冲突的时候,采用了不同的做法,在我们计算出的位置如果不可用,即有元素已经存储在该位置的时候,我们此时对于该位置之后i^2(i=1,2,……n)的位置处进行探测,如果能放入的话就放入,不能放入的话就找下一个位置进行插入。
③开链法:我们对于数组中的每一个元素都存放的是一个指针,当我们通过散列函数计算出位置的时候,就将目标元素串联到该位置处,在元素的插入,搜索和删除等操作中,先通过散列函数计算出该位置,然后就在该位置所存储的链表中进行搜寻,虽然链表的遍历并不是常数时间的复杂度,但是如果链表够短,那么相应的时间也不会很慢,而链表的长度如何判断等具体的操作,会在下面进行解释。
1.2 hashtable的结点设计以及基本结构
在上面的介绍中我们可以看到,由于我们采用的是开链法,因此对于一个基本的hashtable我们需要一个vector,其中所存储的元素类型是指针类型,用于存放链表。
对于一个链表而言,我们仅仅需要一个next指针以及一个value变量即可。
template <class T>
struct hashtable_node
{
hashtable_node* next; // 指向下一个节点
T value; // 储存实值
hashtable_node() = default;
hashtable_node(const T& n) :next(nullptr), value(n) {}
hashtable_node(const hashtable_node& node) :next(node.next), value(node.value) {}
};
1.3 hashtable的迭代器
hashtable的迭代器必须永远维系着与整个vector的关系,并且还能够指向目前所指的结点,也就是说,他不但具有指向当前结点的能力,而且还能够从当前结点返回到vector中。
template <class Value, class Key, class HashFcn, class ExtractKey, class EqualKey, class Alloc>
struct hashtable_iterator {
typedef hashtable<Value, Key, HashFcn, ExtractKey, EqualKey, Alloc> hashtable;
typedef hashtable_iterator<Value, Key, HashFcn, ExtractKey, EqualKey, Alloc> iterator;
typedef const hashtable_iterator<Value, Key, HashFcn, ExtractKey, EqualKey, Alloc> const_iterator;
typedef hashtable_node<Value> node;
typedef ministl::forward_iterator_tag iterator_category;
typedef Value value_type;
typedef ptrdiff_t difference_type;
typedef size_t size_type;
typedef Value& reference;
typedef Value* pointer;
node* cur;//迭代器所指向的结点
hashtable* ht;//保持对容器的连结关系
hashtable_iterator() = default;
hashtable_iterator(node* n, hashtable* tab) : cur(n), ht(tab) { }
reference operator*() const {
return cur->value;
}
pointer operator->()const {
return &(operator*());
}
iterator& operator++();
iterator operator++(int);
bool operator==(const iterator& it) const { return cur == it.cur; }
bool operator!=(const iterator& it) const { return cur != it.cur; }
};
template<class V, class K, class HF, class ExK, class EqK, class A>
hashtable_iterator<V, K, HF, ExK, EqK, A>&
hashtable_iterator<V, K, HF, ExK, EqK, A>::operator++() {
const node* old = cur;
cur = cur->next;
//如果cur等于空就跳到下一个链表中去
if (!cur) {
size_type bucket = ht->bkt_num(old->value);
while (!cur && ++bucket < ht->buckets.size())
cur = ht->buckets[bucket];
}
return *this;
}
template<class V, class K, class HF, class ExK, class EqK, class A>
inline hashtable_iterator<V, K, HF, ExK, EqK, A>
hashtable_iterator<V, K, HF, ExK, EqK, A>::operator++(int) {
iterator tmp = *this;
++* this;
return tmp;
}
在对于前置++的重载中,我们通过bkt_num()函数来定位当前结点在vector中所处的位置,该函数的具体实现如下所示,我们重载了不同的版本来进行取余操作,是因为假如传入的元素无法直接进行取余运算的话,就需要我们通过重载函数来将他转化成我们能够取余的数据,然后再通过散列函数进行计算:
//取余操作
size_type bkt_num_key(const key_type& key, size_t n) const {
return hash(key) % n;
}
size_type bkt_num_key(const key_type& key) const {
return bkt_num_key(key, buckets.size());
}
size_type bkt_num(const value_type& obj, size_t n) const {
return bkt_num_key(get_key(obj), n);
}
size_type bkt_num(const value_type& obj) const {
return bkt_num_key(get_key(obj));
}
1.4 hashtable的数据结构
在之前的描述中,我们知道hashtable是通过一个vector以及vector上的链表来实现的,当我们存储的元素的时候当我们插入的元素个数大于vector数组的长度的时候,这个时候就会进行扩容操作,在SGI STL中,以质数来设计vector大小,并且先将质数以两倍的关系计算好,用于随时访问,同时提供一个函数,用来查询在这些质数中最接近某数并大于某数的质数。
static const int stl_num_primes = 28;
static const unsigned long stl_prime_list[stl_num_primes] =
{
53,97,193,389,769,
1543,3079,6151,12289,24593,
49157,98317,196613,393241,786433,
1572869,3145739,6291469,12582917,
25165843,50331653,100663319,201326611,402653189,
805306457,1610612741,3221225476ul,4294967291ul
};
inline unsigned long stl_next_prime(unsigned long n) {
const unsigned long* first = stl_prime_list;
const unsigned long* last = stl_prime_list + stl_num_primes;
//作用与红黑树中的lower_bound()类似,即找大于first小于last的那个数
const unsigned long* pos = lower_bound(first, last, n);
return pos == last ? *(last - 1) : *pos;
}
具体的数据结构部分如下所示:
template <class Value, class Key, class HashFcn, class ExtractKey, class EqualKey, class Alloc>
class hashtable {
private:
typedef ht_value_traits<Value> value_traits;//元素萃取
typedef Value value_type;
typedef Key key_type;
typedef typename value_traits::mapped_type mapped_type;
typedef HashFcn hasher;//散列函数
typedef EqualKey key_equal;//判断键值是否相等
typedef size_t size_type;
typedef hashtable_iterator<Value, Key, HashFcn, ExtractKey, EqualKey, Alloc> iterator;//迭代器
typedef const hashtable_iterator<Value, Key, HashFcn, ExtractKey, EqualKey, Alloc> const_iterator;
hasher hash;
key_equal equals;
ExtractKey get_key;
typedef hashtable_node<Value> node;
typedef node* node_ptr;
typedef simple_alloc<node, Alloc> node_allocator;
typedef typename node_allocator::pointer pointer;
typedef typename node_allocator::const_pointer const_pointer;
typedef typename node_allocator::reference reference;
typedef typename node_allocator::const_reference const_reference;
typedef typename node_allocator::size_type size_type;
typedef typename node_allocator::difference_type difference_type;
vector<node*, Alloc> buckets;
size_type num_elements;
};
1.5 hashtable的构造与内存管理
在上面的空间配置器中我们定义了一个专属的结点配置器node_allocator,下面是结点的配置函数以及结点的释放函数:
node* new_node(const value_type& obj) {
node* n = node_allocator::allocate();
n->next = nullptr;
ministl::construct(&n->value, obj);
return n;
}
void delete_node(node* n) {
//先调用析构函数 再销毁空间
ministl::destroy(&n->value);
node_allocator::deallocate(n);
}
同样的构造函数如下所示:
hashtable(size_type n, const HashFcn& hf, const EqualKey& eql) :
hash(hf), equals(eql), get_key(ExtractKey()), num_elements(0) {
initialize_buckets(n);
}
hashtable(const hashtable& rhs)
:hash(rhs.hash), equal(rhs.equal),get_key(rhs.get_key), num_elements(rhs.num_elements)
{
copy_init(rhs);
}
hashtable& operator=(const hashtable& rhs);
~hashtable() { clear(); }
在之后的讲解中,我们可以想象当自己在使用一个容器的时候,会进行那些操作,以此顺寻来进行:
①初始化:当我们传入指定的参数想要完成构造的时候,通过参数的类型的不同会调用不同的构造函数,在有参构造函数中,我们会调用第一个构造函数,他调用的是initilize_buckets()函数来完成初始化操作:
void initialize_buckets(size_type n) {
//next_size返回最接近n并大于n的质数
const size_type n_buckets = next_size(n);
//如果n_buckets大于当前容量的话就重建,小于的话就直接返回即可
buckets.resize(n_buckets);
buckets.insert(buckets.end(), n_buckets, (node*)0);
num_elements = 0;
}
如果调用的是拷贝构造函数的话,那么就会调用的是copy_init()函数:
template <class Value, class Key, class HashFcn, class ExtractKey, class EqualKey, class Alloc>
void hashtable<Value, Key, HashFcn,ExtractKey, EqualKey, Alloc>::
copy_init(const hashtable& ht)
{
bucket_size = 0;
//先调整大小
buckets.resize(ht.bucket_size);
//在进行初始化 其中元素均为nullptr
buckets.assign(ht.bucket_size, nullptr);
try
{
for (size_type i = 0; i < ht.bucket_size; ++i)
{
node_ptr cur = ht.buckets[i];
if (cur)
{ // 如果某 bucket 存在链表
auto copy = create_node(cur->value);
buckets[i] = copy;
for (auto next = cur->next; next; cur = next, next = cur->next)
{ //复制链表
copy->next = create_node(next->value);
copy = copy->next;
}
copy->next = nullptr;
}
}
bucket_size = ht.bucket_size;
size = ht.size;
}
catch (...)
{
clear();
}
}
②插入:当我们进行插入操作的时候,首先需要判断一下我们当前容器是否还能进行存储,如果可以进行存储的话,就直接进行存储即可,如果容量不够的话就需要对容器的容量大小进行调整:
template<class V, class K, class HF, class Ex, class Eq, class A>
void hashtable<V, K, HF, Ex, Eq, A>::resize(size_type num_elements_hint) {
//通过元素个数和vector的大小进行比较,如果当前的元素个数大于vector的大小的话
//我们就不进行操作直接返回即可,否则就需要重建vector
const size_type old_n = buckets.size();
if (n > old_n) {
//创建新的vector
vector<node*, A> tmp(n, (node*)0);
for (size_type bucket = 0; bucket < old_n; ++bucket) {
node* first = buckets[bucket];
//将旧的vector上的每条链表上的内容拷贝到新vector上去
while (first) {
size_type new_bucket = bkt_num(first->value, n);
buckets[bucket] = first->next;
first->next = tmp[new_bucket];
tmp[new_bucket] = first;
first = buckets[bucket];
}
//将新旧vector交换
buckets.swap(tmp);
}
}
}
我们需要两种插入方式,通过我们的键值是否能够重复来进行判断,分为可以重复的插入以及不可以重复的插入这两种方式:
pair<iterator, bool> insert_unique(const value_type& obj) {
resize(num_elements + 1);
return insert_unique_noresize(obj);
}
iterator insert_equal(const value_type& obj) {
resize(num_elements + 1);
return insert_equal_noresize(obj);
}
对于不同的插入操作,我们调用不同的函数来完成相应的功能:
//*****************************insert_equal_noresize()****************************
template<class V, class K, class HF, class Ex, class Eq, class A>
typename hashtable<V, K, HF, Ex, Eq, A>::iterator hashtable<V, K ,HF, Ex, Eq, A>::
insert_equal_noresize(const value_type& obj) {
//获取目标元素的位置
const size_type n = bkt_num(obj);
node* first = buckets[n];
for (node* cur = first; cur; cur = cur->next) {
//以下就是执行链表的插入操作
if (equals(get_key(cur->value), get_key(obj))) {
node* tmp = new_node(obj);
tmp->next = cur->next;
cur->next = tmp;
++num_elements;
return iterator(tmp, this);
}
}
//如果我们没有找到与要插入的元素键值相等的元素 那么就直接将目标元素插入到链表头部即可
node* tmp = new_node(obj);
tmp->next = first;
buckets[n] = tmp;
++num_elements;
return iterator(tmp, this);
}
//****************************insert_unique_noresie()***********************
template<class V, class K, class HF, class Ex, class Eq, class A>
pair<typename hashtable<V, K, HF, Ex, Eq, A>::iterator, bool>
hashtable<V, K, HF, Ex, Eq, A>::insert_unique_noresize(const value_type& obj) {
const size_type n = bkt_num(obj);
node* first = buckets[n];
//如果发现有键值相同的元素的话就直接返回
for (node* cur = first; cur; cur = cur->next) {
if (equals(get_key(cur->val), get_key(obj)))
return pair<iterator, bool>(iterator(cur, this), false);
}
//进行插入操作在链表头部
node* tmp = new_node(obj);
tmp->next = first;
buckets[n] = tmp;
++num_elements;
return pair<iterator, bool>(iterator(tmp, this), true);
}
③复制以及删除:删除的时候,我们一定要记得由于链表结点的特殊性,因此对于其 内存的管理一定要谨慎一些,在进行删除操作的时候对于vector上的链表中的元素,我们要逐一的将其释放掉,但是对于vector,我们需要做的仅仅就是将其内部的指针全部变为nullptr即可,仍使其保持原有的大小:
//*****************************clear()**********************************
template<class V, class K, class HF, class Ex, class Eq, class A>
void hashtable<V, K, HF, Ex, Eq, A>::clear() {
for (size_type i = 0; i < buckets.size(); i++) {
node* cur = buckets[i];
//删除list中的每一个结点
while (cur != 0) {
node* next = cur->next;
delete_node(cur);
cur = next;
}
//令bucket内容为null指针
buckets[i] = 0;
}
//令总结点个数为0
num_elements = 0;
}
我们在进行复制的时候,先清除己方的vector,防止出现容器中原有的元素对于复制后的结果产生影响,如果己方的容量大于对方的话,就不懂,如果小于对方的话,就进行调整操作:
template <class V, class K, class HF, class Ex, class Eq, class A>
void hashtable<V, K, HF, Ex, Eq, A>::copy_from(const hashtable& ht) {
//清空
buckets.clear();
//判断是否需要调整
buckets.resize(ht.buckets.size());
//从尾端开始插入n个元素,其值为nullptr
buckets.insert(buckets.end(), ht.buckets.size(), (node*)0);
for (size_type i = 0; i < ht.buckets.size(); ++i) {
//复制vector中的每一个元素
if (const node* cur = ht.buckets[i]) {
node* copy = new_node(cur->value);
buckets[i] = copy;
//然后将vector中的每一个元素的每一个结点复制
for (node* next = cur->next; next; cur = next, next = cur->next) {
copy->next = new_node(next->value);
copy = copy->next;
}
}
}
//改变调整后的大小
num_elements = ht.num_elements;
}
④:查找操作和计数操作
iterator find(const key_type& key) {
//首先寻找落在哪个位置
size_type n = bkt_num_key(key);
node* first;
//比对键值 如果相同就直接跳出返回即可
for (first = buckets[n]; first && !equals(get_key(first->value)), key); first = first->next) {}
return iterator(first, last);
}
size_type count(const key_type& key) const {
//寻找落在哪个位置
const size_type n = bkt_num_key(key);
size_type result = 0;
//键值相同的话则计数加1
for (const node* cur = buckets[n]; cur; cur = cur->next) {
if (equals(get_key(cur->value), key))
++result;
}
return result;
}
1.6 hash function
在之前的描述中,我们通过bkt_num()来完成计算元素位置的作用,而它又是通过调用hash function来完成对应的功能,以此来取得一个可以进行取模运算的数,因此在SGI STL中定义了如下所示的hash function:
inline size_t __stl_hash_string(const char* s)
{
unsigned long h = 0;
for ( ; *s; ++s)
h = 5*h + *s;
return size_t(h);
}
__STL_TEMPLATE_NULL struct hash<char*>
{
size_t operator()(const char* s) const { return __stl_hash_string(s); }
};
__STL_TEMPLATE_NULL struct hash<const char*>
{
size_t operator()(const char* s) const { return __stl_hash_string(s); }
};
__STL_TEMPLATE_NULL struct hash<int> {
size_t operator()(int x) const { return x; }
};
__STL_TEMPLATE_NULL struct hash<unsigned int> {
size_t operator()(unsigned int x) const { return x; }
};
__STL_TEMPLATE_NULL struct hash<long> {
size_t operator()(long x) const { return x; }
};
__STL_TEMPLATE_NULL struct hash<unsigned long> {
size_t operator()(unsigned long x) const { return x; }
2. unordered_set/multiset容器
因为他的基本功能都是通过hashtable来实现的,因此就不多做赘述,仅仅将《STL源码剖析》中的内容放上来了:)
#include "hashtable.h"
template<class Value, class HashFcn = hash<Value>, class EqualKey = equal_to<Value>, class Alloc = alloc>
class unordered_set {
private:
typedef hashtable<Value, Value, HashFcn, identity<Value>, EqualKey, Alloc> ht;
ht rep;
public:
typedef typename ht::key_type key_type;
typedef typename ht::value_type value_type;
typedef typename ht::hasher hasher;
typedef typename ht::key_equal key_equal;
typedef typename ht::size_type size_type;
typedef typename ht::difference_type difference_type;
typedef typename ht::const_pointer pointer;
typedef typename ht::const_pointer const_pointer;
typedef typename ht::const_reference reference;
typedef typename ht::const_reference const_reference;
typedef typename ht::const_iterator iterator;
typedef typename ht::const_iterator const_iterator;
typedef typename ht::node_allocator node_allocator;
public:
unordered_set():rep(100,hasher(), key_equal()) { }
explicit unordered_set(size_type n): rep(n, hasher(), key_equal()) { }
unordered_set(size_type n, const hasher& hf) : rep(n, hf, key_equal()) { }
unordered_set(size_type n, const hasher& hf, const key_equal& eql) : rep(n, hf, eql) { }
template<class InputIterator>
unordered_set(InputIterator f, InputIterator l) : rep(100, hasher(), key_equal()){
rep.insert_unique(f, l);
}
template<class InputIterator>
unordered_set(InputIterator f, InputIterator l, size_type n) : rep(n, hasher(), key_equal()) {
rep.insert_unique(f, l);
}
template<class InputIterator>
unordered_set(InputIterator f, InputIterator l, size_type n, const hasher& hf)
: rep(n, hf, key_equal()){
rep.insert_unique(f, l);
}
template<class InputIterator>
unordered_set(InputIterator f, InputIterator l, size_type n, const hasher& hf, const key_equal& eql)
: rep(n, hf,eql) {
rep.insert_unique(f, l);
}
public:
size_type size() const {
return rep.size();
}
size_type max_size() const {
return rep.max_size();
}
bool empty() const {
return rep.empty();
}
void swap(unordered_set& us) {
rep.swap(us.rep);
}
friend bool operator==(const unordered_set&, const unordered_set&);
iterator begin() const {
return rep.begin();
}
iterator end() const {
return rep.end();
}
public:
pair<iterator, bool> insert(const value_type& obj) {
pair<typename ht::iterator, bool> p = rep.insert_unique(f, l);
return pair<iterator, bool>(p.first, p.second);
}
template <class InputIterator>
void insert(InputIterator f, InputIterator l) {
rep.insert_unique(f, l);
}
pair<iterator, bool> insert_noresize(const value_type& obj) {
pair<typename ht::iterator, bool> p = rep.insert_unique_noresize(obj);
return pair<iterator, bool>(p.first, p.second);
}
iterator find(const key_type& key) const {
return rep.find(key);
}
size_type count(const key_type& key) const {
return rep.count(key);
}
pair<iterator, iterator> equal_range(const key_type& key) {
return rep.equal_range(key);
}
pair<iterator, iterator> equal_range(const key_type& key) const {
return rep.equal_range(key);
}
size_type erase(const key_type& key) {
return rep.erase(key);
}
void erase(iterator it) {
rep.erase(it);
}
void erase(iterator f, iterator l) {
rep.erase(f, l);
}
void clear() { rep.clear(); }
public:
void resize(size_type hint) {
rep.resize(hint);
}
size_type bucket_count() const {
return rep.bucket_count();
}
size_type max_bucket_count() const {
return rep.max_bucket_count();
}
size_type elems_in_bucket(size_type n) const {
return rep.elems_in_bucket(n);
}
};
template<class Value, class HashFcn ,class EqualKey, class Alloc>
inline bool operator==(const unordered_set<Value, HashFcn, EqualKey, Alloc>& us1,
const unordered_set<Value, HashFcn, EqualKey, Alloc>& us2) {
return us1.rep == us2.rep;
}
template<class Value, class HashFcn, class EqualKey, class Alloc>
bool operator!=(const unordered_set<Value, HashFcn, EqualKey, Alloc>& us1,
const unordered_set<Value, HashFcn, EqualKey, Alloc>& us2)
{
return us1.rep != us2.rep;
}
// 重载 ministl 的 swap
template <class Value, class HashFcn, class EqualKey, class Alloc>
void swap(unordered_set<Value, HashFcn, EqualKey, Alloc>& us1,
unordered_set<Value, HashFcn, EqualKey, Alloc>& us2)
{
us1.swap(us2);
}
//****************************** unoredered_mutliset()************************
template<class Value, class HashFcn = hash<Value>, class EqualKey = equal_to<Value>, class Alloc = alloc>
class unordered_mutliset {
private:
typedef hashtable<Value, Value, HashFcn, identity<Value>, EqualKey, Alloc> ht;
ht rep;
public:
typedef typename ht::key_type key_type;
typedef typename ht::value_type value_type;
typedef typename ht::hasher hasher;
typedef typename ht::key_equal key_equal;
typedef typename ht::size_type size_type;
typedef typename ht::difference_type difference_type;
typedef typename ht::const_pointer pointer;
typedef typename ht::const_pointer const_pointer;
typedef typename ht::const_reference reference;
typedef typename ht::const_reference const_reference;
typedef typename ht::const_iterator iterator;
typedef typename ht::const_iterator const_iterator;
typedef typename ht::node_allocator node_allocator;
public:
unordered_mutliset() :rep(100, hasher(), key_equal()) { }
explicit unordered_mutliset(size_type n) : rep(n, hasher(), key_equal()) { }
unordered_mutliset(size_type n, const hasher& hf) : rep(n, hf, key_equal()) { }
unordered_mutliset(size_type n, const hasher& hf, const key_equal& eql) : rep(n, hf, eql) { }
template<class InputIterator>
unordered_mutliset(InputIterator f, InputIterator l) : rep(100, hasher(), key_equal()) {
rep.insert_equal(f, l);
}
template<class InputIterator>
unordered_mutliset(InputIterator f, InputIterator l, size_type n) : rep(n, hasher(), key_equal()) {
rep.insert_equal(f, l);
}
template<class InputIterator>
unordered_mutliset(InputIterator f, InputIterator l, size_type n, const hasher& hf)
: rep(n, hf, key_equal()) {
rep.insert_equal(f, l);
}
template<class InputIterator>
unordered_mutliset(InputIterator f, InputIterator l, size_type n, const hasher& hf, const key_equal& eql)
: rep(n, hf, eql) {
rep.insert_equal(f, l);
}
public:
size_type size() const {
return rep.size();
}
size_type max_size() const {
return rep.max_size();
}
bool empty() const {
return rep.empty();
}
void swap(unordered_set& us) {
rep.swap(us.rep);
}
friend bool operator==(const unordered_set&, const unordered_set&);
iterator begin() const {
return rep.begin();
}
iterator end() const {
return rep.end();
}
public:
pair<iterator, bool> insert(const value_type& obj) {
pair<typename ht::iterator, bool> p = rep.insert_equal(f, l);
return pair<iterator, bool>(p.first, p.second);
}
template <class InputIterator>
void insert(InputIterator f, InputIterator l) {
rep.insert_equal(f, l);
}
pair<iterator, bool> insert_noresize(const value_type& obj) {
pair<typename ht::iterator, bool> p = rep.insert_equal_noresize(obj);
return pair<iterator, bool>(p.first, p.second);
}
iterator find(const key_type& key) const {
return rep.find(key);
}
size_type count(const key_type& key) const {
return rep.count(key);
}
pair<iterator, iterator> equal_range(const key_type& key) const {
return rep.equal_range(key);
}
size_type erase(const key_type& key) {
return rep.erase(key);
}
void erase(iterator it) {
rep.erase(it);
}
void erase(iterator f, iterator l) {
rep.erase(f, l);
}
void clear() { rep.clear(); }
public:
void resize(size_type hint) {
rep.resize(hint);
}
size_type bucket_count() const {
return rep.bucket_count();
}
size_type max_bucket_count() const {
return rep.max_bucket_count();
}
size_type elems_in_bucket(size_type n) const {
return rep.elems_in_bucket(n);
}
};
template<class Value, class HashFcn, class EqualKey, class Alloc>
inline bool operator==(const unordered_mutliset<Value, HashFcn, EqualKey, Alloc>& us1,
const unordered_mutliset<Value, HashFcn, EqualKey, Alloc>& us2) {
return us1.rep == us2.rep;
}
template<class Value, class HashFcn, class EqualKey, class Alloc>
bool operator!=(const unordered_mutliset<Value, HashFcn, EqualKey, Alloc>& us1,
const unordered_mutliset<Value, HashFcn, EqualKey, Alloc>& us2)
{
return us1.rep != us2.rep;
}
// 重载 ministl 的 swap
template <class Value, class HashFcn, class EqualKey, class Alloc>
void swap(unordered_mutliset<Value, HashFcn, EqualKey, Alloc>& us1,
unordered_mutliset<Value, HashFcn, EqualKey, Alloc>& us2)
{
us1.swap(us2);
}
3. unordered_map/multimap容器
同样由于他的基本功能都是通过hashtable来实现的,因此就不多做赘述,仅仅将《STL源码剖析》中的内容放上来了:)
template<class Key, class T, class HashFcn = hash<Key>, class EqualKey = equal_to<Key>, class Alloc = alloc>
class unordered_map
{
private:
typedef hashtable<pair<const Key, T>, Key, HashFcn, select1st<pair<const Key, T>>, EqualKey, Alloc> ht;
ht rep;
public:
typedef typename ht::key_type key_type;
typedef T data_type;
typedef T mapped_type;
typedef typename ht::value_type value_type;
typedef typename ht::hasher hasher;
typedef typename ht::key_equal key_equal;
typedef typename ht::size_type size_type;
typedef typename ht::difference_type difference_type;
typedef typename ht::const_pointer pointer;
typedef typename ht::const_pointer const_pointer;
typedef typename ht::const_reference reference;
typedef typename ht::const_reference const_reference;
typedef typename ht::const_iterator iterator;
typedef typename ht::const_iterator const_iterator;
typedef typename ht::node_allocator node_allocator;
public:
unordered_map() : rep(100, hasher(), key_equal()) { }
explicit unordered_map(size_type n) : rep(n, hasher(), key_equal()) { }
unordered_map(size_type n, const hasher& hf):rep(n, hf, key_equal()) { }
unordered_map(size_type n, const hasher& hf, const key_equal& eql):rep(n, hf, eql) { }
template<class InputIterator>
unordered_map(InputIterator f, InputIterator l) : rep(100, hasher(), key_equal()) {
rep.insert_unique(f, l);
}
template<class InputIterator>
unordered_map(InputIterator f, InputIterator l, size_type n) : rep(n, hasher(), key_equal()) {
rep.insert_unique(f, l);
}
template<class InputIterator>
unordered_map(InputIterator f, InputIterator l, size_type n, const hasher& hf)
: rep(n, hf, key_equal()) {
rep.insert_unique(f, l);
}
template<class InputIterator>
unordered_map(InputIterator f, InputIterator l, size_type n, const hasher& hf, const key_equal& eql)
: rep(n, hf, eql) {
rep.insert_unique(f, l);
}
public:
size_type size() const {
return rep.size();
}
size_type max_size() const {
return rep.max_size();
}
bool empty() const {
return rep.empty();
}
void swap(unordered_set& us) {
rep.swap(us.rep);
}
friend bool operator==(const unordered_map& um1, const unordered_map& um2) {
return um1 == um2;
}
mapped_type& at(const key_type& key)
{
iterator it = rep.find(key);
if (it.node == nullptr) {
cout << "unordered_map<Key, T> no such element exists" << endl;
exit(0);
}
return it->second;
}
const mapped_type& at(const key_type& key) const
{
iterator it = rep.find(key);
if (it.node == nullptr) {
cout << "unordered_map<Key, T> no such element exists" << endl;
exit(0);
}
return it->second;
}
mapped_type& operator[](const key_type& key)
{
iterator it = rep.find(key);
if (it.node == nullptr)
it = rep.insert_unique(key).first;
return it->second;
}
iterator begin() const {
return rep.begin();
}
iterator end() const {
return rep.end();
}
const_iterator begin() const {
return rep.begin();
}
const_iterator end() const {
return rep.end();
}
public:
pair<iterator, bool> insert(const value_type& obj) {
return rep.insert_unique(obj);
}
template <class InputIterator>
void insert(InputIterator f, InputIterator l) {
rep.insert_unique(f, l);
}
pair<iterator, bool> insert_noresize(const value_type& obj) {
pair<typename ht::iterator, bool> p = rep.insert_unique_noresize(obj);
return pair<iterator, bool>(p.first, p.second);
}
iterator find(const key_type& key) const {
return rep.find(key);
}
size_type count(const key_type& key) const {
return rep.count(key);
}
pair<iterator, iterator> equal_range(const key_type& key) {
return rep.equal_range(key);
}
pair<iterator, iterator> equal_range(const key_type& key) const {
return rep.equal_range(key);
}
size_type erase(const key_type& key) {
return rep.erase(key);
}
void erase(iterator it) {
rep.erase(it);
}
void erase(iterator f, iterator l) {
rep.erase(f, l);
}
void clear() { rep.clear(); }
public:
void resize(size_type hint) {
rep.resize(hint);
}
size_type bucket_count() const {
return rep.bucket_count();
}
size_type max_bucket_count() const {
return rep.max_bucket_count();
}
size_type elems_in_bucket(size_type n) const {
return rep.elems_in_bucket(n);
}
};
template<class Value, class HashFcn, class EqualKey, class Alloc>
inline bool operator==(const unordered_map<Value, HashFcn, EqualKey, Alloc>& us1,
const unordered_map<Value, HashFcn, EqualKey, Alloc>& us2) {
return us1.rep == us2.rep;
}
template<class Value, class HashFcn, class EqualKey, class Alloc>
bool operator!=(const unordered_map<Value, HashFcn, EqualKey, Alloc>& us1,
const unordered_map<Value, HashFcn, EqualKey, Alloc>& us2)
{
return us1.rep != us2.rep;
}
// 重载 ministl 的 swap
template <class Value, class HashFcn, class EqualKey, class Alloc>
void swap(unordered_map<Value, HashFcn, EqualKey, Alloc>& us1,
unordered_map<Value, HashFcn, EqualKey, Alloc>& us2)
{
us1.swap(us2);
}
//****************************** unordered_mutlimap() **************************
template<class Key, class T, class HashFcn = hash<Key>, class EqualKey = equal_to<Key>, class Alloc = alloc>
class unordered_multimap
{
private:
typedef hashtable<pair<const Key, T>, Key, HashFcn, select1st<pair<const Key, T>>, EqualKey, Alloc> ht;
ht rep;
public:
typedef typename ht::key_type key_type;
typedef T data_type;
typedef T mapped_type;
typedef typename ht::value_type value_type;
typedef typename ht::hasher hasher;
typedef typename ht::key_equal key_equal;
typedef typename ht::size_type size_type;
typedef typename ht::difference_type difference_type;
typedef typename ht::const_pointer pointer;
typedef typename ht::const_pointer const_pointer;
typedef typename ht::const_reference reference;
typedef typename ht::const_reference const_reference;
typedef typename ht::const_iterator iterator;
typedef typename ht::const_iterator const_iterator;
typedef typename ht::node_allocator node_allocator;
public:
unordered_multimap() : rep(100, hasher(), key_equal()) { }
explicit unordered_multimap(size_type n) : rep(n, hasher(), key_equal()) { }
unordered_multimap(size_type n, const hasher& hf) :rep(n, hf, key_equal()) { }
unordered_multimap(size_type n, const hasher& hf, const key_equal& eql) :rep(n, hf, eql) { }
template<class InputIterator>
unordered_multimap(InputIterator f, InputIterator l) : rep(100, hasher(), key_equal()) {
rep.insert_equal(f, l);
}
template<class InputIterator>
unordered_multimap(InputIterator f, InputIterator l, size_type n) : rep(n, hasher(), key_equal()) {
rep.insert_equal(f, l);
}
template<class InputIterator>
unordered_multimap(InputIterator f, InputIterator l, size_type n, const hasher& hf)
: rep(n, hf, key_equal()) {
rep.insert_equal(f, l);
}
template<class InputIterator>
unordered_multimap(InputIterator f, InputIterator l, size_type n, const hasher& hf, const key_equal& eql)
: rep(n, hf, eql) {
rep.insert_equal(f, l);
}
public:
size_type size() const {
return rep.size();
}
size_type max_size() const {
return rep.max_size();
}
bool empty() const {
return rep.empty();
}
void swap(unordered_set& us) {
rep.swap(us.rep);
}
mapped_type& at(const key_type& key)
{
iterator it = rep.find(key);
if (it.node == nullptr) {
cout << "unordered_map<Key, T> no such element exists" << endl;
exit(0);
}
return it->second;
}
const mapped_type& at(const key_type& key) const
{
iterator it = rep.find(key);
if (it.node == nullptr) {
cout << "unordered_map<Key, T> no such element exists" << endl;
exit(0);
}
return it->second;
}
mapped_type& operator[](const key_type& key)
{
iterator it = rep.find(key);
if (it.node == nullptr)
it = rep.insert_equal(key).first;
return it->second;
}
friend bool operator==(const unordered_multimap& um1,const unordered_multimap& um2) {
return um1.rep.equal_range_unique(um2);
}
iterator begin() const {
return rep.begin();
}
iterator end() const {
return rep.end();
}
const_iterator begin() const {
return rep.begin();
}
const_iterator end() const {
return rep.end();
}
public:
pair<iterator, bool> insert(const value_type& obj) {
return rep.insert_equal(obj);
}
template <class InputIterator>
void insert(InputIterator f, InputIterator l) {
rep.insert_equal(f, l);
}
pair<iterator, bool> insert_noresize(const value_type& obj) {
pair<typename ht::iterator, bool> p = rep.insert_equal_noresize(obj);
return pair<iterator, bool>(p.first, p.second);
}
iterator find(const key_type& key) const {
return rep.find(key);
}
size_type count(const key_type& key) const {
return rep.count(key);
}
pair<iterator, iterator> equal_range(const key_type& key) {
return rep.equal_range(key);
}
pair<iterator, iterator> equal_range(const key_type& key) const {
return rep.equal_range(key);
}
size_type erase(const key_type& key) {
return rep.erase(key);
}
void erase(iterator it) {
rep.erase(it);
}
void erase(iterator f, iterator l) {
rep.erase(f, l);
}
void clear() { rep.clear(); }
public:
void resize(size_type hint) {
rep.resize(hint);
}
size_type bucket_count() const {
return rep.bucket_count();
}
size_type max_bucket_count() const {
return rep.max_bucket_count();
}
size_type elems_in_bucket(size_type n) const {
return rep.elems_in_bucket(n);
}
};
template<class Value, class HashFcn, class EqualKey, class Alloc>
inline bool operator==(const unordered_multimap<Value, HashFcn, EqualKey, Alloc>& us1,
const unordered_multimap<Value, HashFcn, EqualKey, Alloc>& us2) {
return us1.rep == us2.rep;
}
template<class Value, class HashFcn, class EqualKey, class Alloc>
bool operator!=(const unordered_multimap<Value, HashFcn, EqualKey, Alloc>& us1,
const unordered_multimap<Value, HashFcn, EqualKey, Alloc>& us2)
{
return us1.rep != us2.rep;
}
// 重载 ministl 的 swap
template <class Value, class HashFcn, class EqualKey, class Alloc>
void swap(unordered_multimap<Value, HashFcn, EqualKey, Alloc>& us1,
unordered_multimap<Value, HashFcn, EqualKey, Alloc>& us2)
{
us1.swap(us2);
}
参考:侯捷老师《STL源码剖析》
TinySTL