STL源码剖析:4.关联式容器(下)


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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值