hash表对我来说是闻名不见面,因为stl提供的基于rbtree的容器目前是满足我的工作需求的。最近闲得慌,于是看看非标准(C98)容器hash_set的实现。另外我所知道使用hash表的还有MFC里的CMap,MFC的实现比较朴素,代码一看就懂,就不分析了。
源码基于VS2008 SP1。
为了表达明确,假设比较器为less<T>。
结构:
类似于基于rbtree的set、map。基于hash表的hash_set、hash_map都是提供接口的类,底层都是继承于_Hash这个模板类。最终的接口也和rbtree的实现一样,甚至insert都有重载一个hint的迭代器,尽管_Hash实际上都没用到这个迭代器。
hash表的成员讲,一般都是数组+链表指针(如MFC里的CMap)。然而hash_set用list<T>作为开链,并管理所有实际元素,用vector<list<T>::iterator>来代替传统数组做bucket。这和stl很多时候做法一致:用现有容器,而不是裸概念。如用deque实现queue,用vector+xx_heap系列函数实现priority_queue。
构造:
void _Init(size_type _Buckets = min_buckets)
{ // initialize hash table with _Buckets buckets, leave list alone
_Vec.assign(_Buckets + 1, end());
_Mask = _Buckets - 1;
_Maxidx = _Buckets;
}
代码比较少,主要是初始化vector的数量,和最大桶索引,其中_Mask用来取代%操作。a % (2^n) == (a & (2^n - 1))
insert
_Pairib insert(const value_type& _Val)
{ // try to insert node with value _Val
return (_Insert(_Val, end()));
}
_Pairib _Insert(const value_type& _Val, iterator _Where)
{ // try to insert (possibly existing) node with value _Val
size_type _Bucket = _Hashval(this->_Kfn(_Val));
iterator _Plist = _Get_iter_from_vec(_Vec[_Bucket + 1]);
for (; _Plist != _Get_iter_from_vec(_Vec[_Bucket]); )
if (this->comp(this->_Kfn(_Val), this->_Kfn(*--_Plist)))
; // still too high in bucket list
else if (_Multi
|| this->comp(this->_Kfn(*_Plist), this->_Kfn(_Val)))
{ // found insertion point, back