STL源码剖析——hash_map解析

目录

hash table 的节点定义:

hashtable 的迭代器:

hashtable 的数据结构

rehash和插入操作

复制与整体删除

C++实现的hashmap简易类


STL的的hash_set,hash_map都是基于hash_table实现的,SGI STL 中哈希表采用链接法解决冲突。结构中维护了一个 vector,vector 中每一个元素称为一个桶(bucket),它包含的是一个链表的第一个节点。

hash table 的节点定义:

template <class Value>
struct __hashtable_node
{
  __hashtable_node* next; //下一个节点
  Value val; //键值
}; 

注意,在hashmap中会把Value替换成pair<key,value>键值对,也就是节点保存不仅是value但是hash映射时只用key 

bucket 所维护的正是上述的 hash table node。

从上图可知,buckets vector 中存放的是bucket,然后每个bucket 存放链表(如果有的话)。通俗的说bucket 就是动态array 中的一个元素。

hashtable 的迭代器:

这里解释下各个参数含义,HashFunc是模版函数,可以看成重写了()运算符的函数类,后面会定义一个HashFunc成员hash可以看成函数对象用来计算不同类型的hashcode,STL重写自定义类型的hash特化版本,ExtractKey是用来得到key的方法(后面hashmap用到了select1st,set用到了identity),EqualKey是重写的判断key是否相等的方法,Alloc则是空间配置器。

template <class Value, class Key, class HashFcn,
class ExtractKey, class EqualKey, class Alloc>
struct __hashtable_iterator {
	//typedef 定义
	typedef hashtable<Value, Key, HashFcn, ExtractKey, EqualKey, Alloc>
	hashtable;
	typedef __hashtable_iterator<Value, Key, HashFcn,
		ExtractKey, EqualKey, Alloc>
		iterator;
	typedef __hashtable_const_iterator<Value, Key, HashFcn,
		ExtractKey, EqualKey, Alloc>
		const_iterator;
	typedef __hashtable_node<Value> node;
 
	typedef 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;  //保持对容器的连结关系(因为可能需要从bucket跳到bucket)
 
	/*构造函数*/
	__hashtable_iterator(node* n, hashtable* tab) : cur(n), ht(tab) {}
	__hashtable_iterator() {}
 
	/*运算符重载*/
	reference operator*() const { return cur->val; }
 
	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; }
};

hashtable 迭代器必须永远维系着与整个“bucket vector” 的关系,并记录目前所指的节点。其前进操作是首先尝试从目前所指的节点出发,前进一个位置(节点),由于节点被安置于 list 内,所以利用节点的 next 指针即可轻易达成前进操作。如果目前节点正巧是list 的尾端,就跳至下一个bucket 身上,那正是指向下一个list 的头部节点。上面的 hashtable* ht; 就是为这点添加的。
 

/*前缀++*/
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;//更新为当前bucket链表中的下一个节点
 
	if (!cur) {//如果该节点恰好为list 的尾端
		size_type bucket = ht->bkt_num(old->val);//通过散列函数查找键值对应的bucket
		while (!cur && ++bucket < ht->buckets.size())//根据元素值定位下一个bucket的起头处
			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;//调用operator++
	return tmp;//返回自增前的值
}

hashtable 的数据结构

由于在STL源码中 hashtable 的操作函数都定义在结构体类,这里我们只摘取一部分,具体函数实现在后面介绍,核心就是vector实现的hash表每个表节点是node链表,并num表示节点数,用取余和素数设置表长来实现hash方法

/*hashtable节点结构,处于篇幅考虑,这里省去对应const类型code*/
template <class Value, class Key, class HashFcn,
class ExtractKey, class EqualKey,
class Alloc>
class hashtable {
public:
	typedef Key key_type;//节点的键值型别
	typedef Value value_type;//节点的实值型别
	typedef HashFcn hasher;//散列函数的函数型别
	typedef EqualKey key_equal;//判断键值相同与否的方法
 
	/*
	ExtractKey:从节点中取出键值的方法
	Alloc:空间配置器,缺省使用std::alloc
	*/
 
	typedef size_t            size_type;
	typedef ptrdiff_t         difference_type;
	typedef value_type*       pointer;
	typedef const value_type* const_pointer;
	typedef value_type&       reference;
	typedef const value_type& const_reference;
 
	hasher hash_funct() const { return hash; }
	key_equal key_eq() const { return equals; }
 
private:
	hasher hash;
	key_equal equals;
	ExtractKey get_key;
 
	typedef __hashtable_node<Value> node;
	typedef simple_alloc<node, Alloc> node_allocator;
 
	vector<node*, Alloc> buckets;//桶(动态array)
	size_type num_elements;//散列表中所有元素的个数
 
public:
	/*typedef 定义*/
	typedef __hashtable_iterator<Value, Key, HashFcn, ExtractKey, EqualKey,
		Alloc>
		iterator;
 
	typedef __hashtable_const_iterator<Value, Key, HashFcn, ExtractKey, EqualKey,
		Alloc>
		const_iterator;
 
	friend struct
		__hashtable_iterator<Value, Key, HashFcn, ExtractKey, EqualKey, Alloc>;
	friend struct
		__hashtable_const_iterator<Value, Key, HashFcn, ExtractKey, EqualKey, Alloc>;
 
public:
	/*构造函数*/
	hashtable(size_type n,
		const HashFcn&    hf,
		const EqualKey&   eql,
		const ExtractKey& ext)
		: hash(hf), equals(eql), get_key(ext), num_elements(0)
	{
		initialize_buckets(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& ht)
		: hash(ht.hash), equals(ht.equals), get_key(ht.get_key), num_elements(0)
	{
		copy_from(ht);
	}
	/*赋值构造函数*/
	hashtable& operator= (const hashtable& ht)
	{
		if (&ht != this) {
			clear();//清除原有数据
			hash = ht.hash;
			equals = ht.equals;
			get_key = ht.get_key;
			copy_from(ht);
		}
		return *this;
	}
	/*析构函数*/
	~hashtable() { clear(); }
 
	size_type size() const { return num_elements; }//实际元素
	size_type max_size() const { return size_type(-1); }
	bool empty() const { return size() == 0; }//判断是否为空
 
	/*类里面,this* 与ht交换,浅交换*/
	void swap(hashtable& ht)
	{
		::swap(hash, ht.hash);
		::swap(equals, ht.equals);
		::swap(get_key, ht.get_key);
		buckets.swap(ht.buckets);
		::swap(num_elements, ht.num_elements);
	}
 
	/*迭代器有效首位置*/
	iterator begin()
	{
		for (size_type n = 0; n < buckets.size(); ++n)
		if (buckets[n])
			/*下面实质就是__hashtable_iterator(node* n, hashtable* tab) : cur(n), ht(tab){}*/
			return iterator(buckets[n], this);
		return end();
	}
 
	/*迭代器尾端位置的下一位置,其实是空位置*/
	iterator end() { return iterator(0, this); }
 
	/*==运算符重载*/
	friend bool operator== (const hashtable<Value, Key,
		HashFcn, ExtractKey, EqualKey,
		Alloc>&,
		const hashtable<Value, Key,
		HashFcn, ExtractKey, EqualKey,
		Alloc>&);
 
public:
	/*返回桶子的个数(vector的大小)*/
	size_type bucket_count() const { return buckets.size(); }
	/*可能的最大值(质数表里的最大值)*/
	size_type max_bucket_count() const
	{
		return __stl_prime_list[__stl_num_primes - 1];
	}
 
	/*指定第bucket个桶中节点的个数*/
	size_type elems_in_bucket(size_type bucket) const
	{
		size_type result = 0;
		for (node* cur = buckets[bucket]; cur; cur = cur->next)//某bucket中链表节点的个数
			result += 1;
		return result;
	}
	/*插入单个元素,不允许重复*/
	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);
	}
 
	pair<iterator, bool> insert_unique_noresize(const value_type& obj);
	iterator insert_equal_noresize(const value_type& obj);
 
	/*下面都是批量元素插入*/
	……
 
	/*两个连续存储区之间的元素插入*/
	void insert_unique(const value_type* f, const value_type* l)
	{
		size_type n = l - f;
		resize(num_elements + n);
		for (; n > 0; --n, ++f)
			insert_unique_noresize(*f);
	}
 
	void insert_equal(const value_type* f, const value_type* l)
	{
		size_type n = l - f;
		resize(num_elements + n);
		for (; n > 0; --n, ++f)
			insert_equal_noresize(*f);
	}
 
	/*插入两个迭代器之间的元素,不允许重复*/
	void insert_unique(const_iterator f, const_iterator l)
	{
		size_type n = 0;
		distance(f, l, n);//迭代器距离
		resize(num_elements + n);
		for (; n > 0; --n, ++f)
			insert_unique_noresize(*f);//两个迭代器之间的元素逐步插入
	}
 
	/*插入两个迭代器之间的元素,允许重复*/
	void insert_equal(const_iterator f, const_iterator l)
	{
		size_type n = 0;
		distance(f, l, n);//迭代器距离
		resize(num_elements + n);
		for (; n > 0; --n, ++f)
			insert_equal_noresize(*f);//两个迭代器之间的元素逐步插入(这里的两个迭代器之间不是线性的)
	}
 
	reference find_or_insert(const value_type& obj);
 
	/*查找*/
	iterator find(const key_type& key)
	{
		size_type n = bkt_num_key(key);//定位
		node* first;
		/*直接在下面循环中完成搜索*/
		for (first = buckets[n];
			first && !equals(get_key(first->val), key);
			first = first->next)
		{
		}
		return iterator(first, this);
	}
 
	/*统计与键值key相等的元素的个数*/
	size_type count(const key_type& key) const
	{
		const size_type n = bkt_num_key(key);//定位
		size_type result = 0;
 
		for (const node* cur = buckets[n]; cur; cur = cur->next)
		if (equals(get_key(cur->val), key))
			++result;
		return result;
	}
 
	pair<iterator, iterator> equal_range(const key_type& key);
	pair<const_iterator, const_iterator> equal_range(const key_type& key) const;
 
	size_type erase(const key_type& key);
	void erase(const iterator& it);
	void erase(iterator first, iterator last);
 
	void erase(const const_iterator& it);
	void erase(const_iterator first, const_iterator last);
 
	void resize(size_type num_elements_hint);
	void clear();
 
private:
	size_type next_size(size_type n) const { return __stl_next_prime(n); }
 
	/*buckes初始化*/
	void initialize_buckets(size_type n)
	{
		const size_type n_buckets = next_size(n);//质数表里大于等于该数的最小质数
		buckets.reserve(n_buckets);//调整buckets大小(buckets其实就是一个vector)
		buckets.insert(buckets.end(), n_buckets, (node*)0);
		num_elements = 0;
	}
 
	/*下面近似是散列函数,判知元素的落脚处*/
	/*只接受键值*/
	size_type bkt_num_key(const key_type& key) const
	{
		return bkt_num_key(key, buckets.size());
	}
	/*只接受实值(value)*/
	size_type bkt_num(const value_type& obj) const
	{
		return bkt_num_key(get_key(obj));
	}
	/*接受键值和 buckets个数*/
	size_type bkt_num_key(const key_type& key, size_t n) const
	{
		return hash(key) % n;
	}
	/*接受实值(value)和 buckets个数*/
	size_type bkt_num(const value_type& obj, size_t n) const
	{
		return bkt_num_key(get_key(obj), n);
	}
 
	/*创建新节点*/
	node* new_node(const value_type& obj)
	{
		node* n = node_allocator::allocate();
		n->next = 0;
 
		try {
			construct(&n->val, obj);
			return n;
		}
		catch (...) {
			node_allocator::deallocate(n);
			throw;
		}
	}
	/*销毁*/
	void delete_node(node* n)
	{
		destroy(&n->val);
		node_allocator::deallocate(n);
	}
 
	void erase_bucket(const size_type n, node* first, node* last);
	void erase_bucket(const size_type n, node* last);
	void copy_from(const hashtable& ht);
};

质数表:SGI STL 中的hashtable 是采用质数来设定表格大小

/*质数表*/
// Note: assumes long is at least 32 bits.
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, 3221225473, 4294967291
};
 
/*以下找出上述28个质数之中,最接近并大于 n的那个质数(有的话),没有取最大*/
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;//尾的下一位置
	/*泛型算法,返回一个迭代器,指向第一个不小于 n的元素*/
	const unsigned long* pos = lower_bound(first, last, n);
	return pos == last ? *(last - 1) : *pos;//如果没有比它大的就取最大的
}

这里考虑到元素的各种型别,将散列函数封装起来

template <class Key> struct hash { };
/*字符串类型*/
inline size_t __stl_hash_string(const char* s)
{
	/*处理方法*/
	unsigned long h = 0;
	for (; *s; ++s)
		h = 5 * h + *s;
 
	return size_t(h);
}
 
/*字符串类型,则调用上面处理方法*/
struct hash<char*>
{
	size_t operator()(const char* s) const { return __stl_hash_string(s); }
};
 
struct hash<const char*>
{
	size_t operator()(const char* s) const { return __stl_hash_string(s); }
};
 
/*下面各种类型直接返回,无需额外处理*/
struct hash<char> {
	size_t operator()(char x) const { return x; }
};
struct hash<unsigned char> {
	size_t operator()(unsigned char x) const { return x; }
};
struct hash<signed char> {
	size_t operator()(unsigned char x) const { return x; }
};
struct hash<short> {
	size_t operator()(short x) const { return x; }
};
struct hash<unsigned short> {
	size_t operator()(unsigned short x) const { return x; }
};
struct hash<int> {
	size_t operator()(int x) const { return x; }
};
struct hash<unsigned int> {
	size_t operator()(unsigned int x) const { return x; }
};
struct hash<long> {
	size_t operator()(long x) const { return x; }
};
struct hash<unsigned long> {
	size_t operator()(unsigned long x) const { return x; }
};

rehash和插入操作

核心方法:每次插入会先调用resize判断是否rehash然后再插入;rehash思路很简单,就是判断节点数大于hash表长时就扩容扩容为节点节点数的下一个质数,扩容时新建一个临时hash的vector,遍历原hash表,重新计算hash值挂到临时hash中,最后swap交换

/*以下函数判断是否需要重建表格。如果不需要,立即返回。如果需要,就重建*/
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)
{
	const size_type old_n = buckets.size();//bucket vector 的大小
	/*如果元素个数(把新增元素计入后)比bucket vector 大,则需要重建表格*/
	if (num_elements_hint > old_n) {
		const size_type n = next_size(num_elements_hint);//找出下一个质数
		
		if (n > old_n) { //old_n不是质数表里面的最大值时,才可扩展
			vector<node*, A> tmp(n, (node*)0);//设立新的bucket vector,大小为n
			try {
				//以下处理每一个旧的bucket
				for (size_type bucket = 0; bucket < old_n; ++bucket) {
                    //指向节点所对应之串行(链表)的起始节点
					node* first = buckets[bucket];
					while (first) {//处理单个bucket中的链表
                        //找出节点落在哪一个新的bucket内
						size_type new_bucket = bkt_num(first->val, n);
                        //令旧bucket指向其所对应的链表的下一个节点,以便迭代处理
						buckets[bucket] = first->next;
 
						/*下面将当前节点插入到新的bucket内,成为其对应链表的第一个节点,这里的实现比较巧妙
						相当于插入新节点到新bucket vector中,新插入的元素插入到链表的首位置,这里不同于一般的插入的是,
						由于之前已有元素占据空间,这里只是修改节点指针指向*/
						first->next = tmp[new_bucket];
						tmp[new_bucket] = first;
 
						first = buckets[bucket];//回到旧bucket所指的待处理链表,准备处理下一个节点
						                        //first = old_first->next;
					}
				}
				buckets.swap(tmp);//vector::swap 新旧两个buckets 对调(浅修改)
				/*对调两方如果大小不同,大的会变小,小的会变大,离开时释放local tmp 的内存*/
			}
			catch (...) {
				for (size_type bucket = 0; bucket < tmp.size(); ++bucket) {
					while (tmp[bucket]) {
						node* next = tmp[bucket]->next;
						delete_node(tmp[bucket]);
						tmp[bucket] = next;
					}
				}
				throw;
			}
		}
	}
}
 
                                                                                                                                                   

插入分是否允许键值重复分为两种,前者需要遍历当前链表每个值来判断是否同一个键 

/*插入元素,不允许重复*/
template <class V, class K, class HF, class Ex, class Eq, class A>
pair<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);//定位bucket
	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);//产生新节点 node_allocator::allocate()
	/*先插入节点放在链表最前面*/
	tmp->next = first;
	buckets[n] = tmp;
	++num_elements;//元素个数增加
	return pair<iterator, bool>(iterator(tmp, this), true);
}
 
/*插入元素,允许重复*/
template <class V, class K, class HF, class Ex, class Eq, class A>
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);//定位bucket
	node* first = buckets[n];//链表头节点
 
	for (node* cur = first; cur; cur = cur->next)
	if (equals(get_key(cur->val), get_key(obj))) {//如果插入元素是重复的(与cur->val重复)
		node* tmp = new_node(obj);
		tmp->next = cur->next;//新增元素插入重复元素的后面
		cur->next = tmp;
		++num_elements;
		return iterator(tmp, this);
	}
	//没有重复,等同于insert_unique_noresize()
	node* tmp = new_node(obj);
	tmp->next = first;
	buckets[n] = tmp;
	++num_elements;
	return iterator(tmp, this);
}      

复制与整体删除

/*复制*/
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.reserve(ht.buckets.size());//bucket vector 空间调整为 ht对应的大小
	buckets.insert(buckets.end(), ht.buckets.size(), (node*)0);//从尾端开始插入n个元素,其值为NULL指针
	//此时buckets vector 为空,所以所谓尾端,就是起头处
	try {
		for (size_type i = 0; i < ht.buckets.size(); ++i) {
			//这里是复制vector 中的每一个元素
			if (const node* cur = ht.buckets[i]) {//如果该位置不为空,表明有元素需要复制
				node* copy = new_node(cur->val);
				buckets[i] = copy;//先是bucket vector 中的元素
 
				//然后是针对同一个bucket list,复制每一个节点
				for (node* next = cur->next; next; cur = next, next = cur->next) {
					copy->next = new_node(next->val);
					copy = copy->next;
				}
			}
		}
		num_elements = ht.num_elements;
	}
	catch (...) {
		clear();
		throw;
	}
}
 
/*删除第 n 个bucket中的所有元素(链表也要逐个删除)*/
template <class V, class K, class HF, class Ex, class Eq, class A>
void
hashtable<V, K, HF, Ex, Eq, A>::erase_bucket(const size_type n, node* last)
{
	node* cur = buckets[n];
	while (cur != last) {
		node* next = cur->next;
		delete_node(cur);
		cur = next;
		buckets[n] = cur;
		--num_elements;
	}
}
 
/*整体删除,考虑链表的存在,需要逐个删除释放内存*/
template <class V, class K, class HF, class Ex, class Eq, class A>
void hashtable<V, K, HF, Ex, Eq, A>::clear()
{
	//针对每一个bucket
	for (size_type i = 0; i < buckets.size(); ++i) {
		node* cur = buckets[i];
		//删除释放该桶中的链表中的所有节点
		while (cur != 0) {
			node* next = cur->next;
			delete_node(cur);
			cur = next;
		}
		buckets[i] = 0;
	}
	num_elements = 0;
	//此时bucket vector 空间并未释放
}

C++实现的hashmap简易类

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
static int prime[28] =
{
    57,        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
};
int genPrime(int* p){
    int cnt=0;
    int n=10000;
    vector<int> isPrime(n+1);
    for(int i=2;i<=n;i++){
        if(isPrime[i]==0){
            p[cnt++]=i;
            for(int j=i*i;j<=n;j+=i){
                isPrime[j]=1;
            }
        }
    }
}
int find_next(int cur){
    int i=0;
    for(;i<28;i++){
        if(cur<prime[i])break;

    }
    return prime[i];
}
template <class Key>
struct Hash{};
template<> struct Hash<int>{
    int operator()(int x) const{return x;}
};
template<class Key,class Value,class HashFunc=Hash<Key>>
class HashMap
{
public:
    template<class _Key,class _Value>
    struct Node{
        _Value value;
        _Key key;
        Node* next;
    };
    typedef Node<Key,Value> node;
    vector<node*> buckets;
    int nums;
    HashFunc Hash;

public:
    HashMap(){
        buckets.reserve(29);
        buckets.insert(buckets.end(),29,(node*)0);
        nums=0;
    }
    int bkt_num_key(const Key& key, int n)
    {
        return (key) % n;
    }
	inline int getNext(int n)
    {
        int* pos = lower_bound(prime, prime+28, n);
        return *pos;
    }
    void resize(int num_elements){
        int old_n=buckets.size();

        if(num_elements>old_n){

            int n=find_next(num_elements);
            vector<node*> tmp(n,(node*)0);
            for (int bucket = 0; bucket < old_n; ++bucket) {
					node* first = buckets[bucket];
					while (first) {
						int new_bucket = bkt_num_key(first->key, n);
						buckets[bucket] = first->next;
						first->next = tmp[new_bucket];
						tmp[new_bucket] = first;
						first = buckets[bucket];
					}
            }
				buckets.swap(tmp);

        }
    }

    bool insertKV(const Key& key,const Value& value){
        resize(nums+1);
        int len=buckets.size();
        int n = bkt_num_key(key,len);
        node* first = buckets[n];
        for (node* cur = first; cur; cur = cur->next)
        if (cur->key==key)
            return false;
        node* tmp = new Node<Key,Value>;
        tmp->key=key;tmp->value=value;
        tmp->next = first;
        buckets[n] = tmp;
        ++nums;
        return true;
    }
    Value get(const Key& key){
        int len=buckets.size();
        int n = bkt_num_key(key,len);
        cout<<n<<endl;
        node* first;
        for(first=buckets[n];first&&key!=first->key;first=first->next);
        if(first==0) return 0;
        cout<<first->value<<endl;
        return first->value;
    }
    Value& operator[](const Key& key){
        int len=buckets.size();
        int n = bkt_num_key(key,len);
        cout<<n<<endl;
        node* first;
        for(first=buckets[n];first&&key!=first->key;first=first->next);
        if(first==0) insertKV(key,0);

        cout<<first->value<<endl;
        return first->value;
    }
};
int main()
{
    HashMap<int,int> mp;

    mp.insertKV(3,4);
    mp.insertKV(1,3);
    mp.insertKV(0,3);

    cout<<mp.get(3);
    cout<<mp[1];
    return 0;

}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值