STL源码剖析 hashtable
生活随笔
收集整理的這篇文章主要介紹了
STL源码剖析 hashtable
小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
- 二叉搜索樹具有對數(shù)平均時(shí)間的表現(xiàn),但是這個(gè)需要滿足的假設(shè)前提是輸入的數(shù)據(jù)需要具備隨機(jī)性
- hashtable 散列表這種結(jié)構(gòu)在插入、刪除、搜尋等操作層面上也具有常數(shù)平均時(shí)間的表現(xiàn)。而且不需要依賴元素的隨機(jī)性,這種表現(xiàn)是以統(tǒng)計(jì)為基礎(chǔ)的
hashtable的概述
- hashtable可提供對任何有名項(xiàng)的存取和刪除操作
- 因?yàn)椴僮鞯膶ο笫怯忻?xiàng),因此hashtable可以作為一種字典結(jié)構(gòu)
- 將一個(gè)元素映射成為一個(gè) “大小可以接受的索引”簡稱為hash function散列函數(shù)
- 考慮到元素的個(gè)數(shù)大于array的容量,可能有不同的元素被映射到相同的位置,簡稱為碰撞
- 解決碰撞的方法有很多,線性探測、二次探測、開鏈
線性探測
- 負(fù)載系數(shù):元素的個(gè)數(shù) 除以表格的大小 ,負(fù)載系數(shù)介于0-1,除非使用開鏈法
- 使用線性探測時(shí),根據(jù)散列函數(shù)計(jì)算得到的位置已經(jīng)存在了元素,就需要循環(huán)往下一一尋找,如果到達(dá)array的尾端,就需要繞回到頭部繼續(xù)尋找,直到找到一個(gè)可用的空間為止。
- 元素的搜尋也是類似
- 元素的刪除采用惰性機(jī)制,只標(biāo)記刪除的記號,實(shí)際真正的刪除操作需要等待表格重新整理時(shí)才可以進(jìn)行
- 需要兩個(gè)假設(shè):1,表格足夠大;2,每個(gè)元素都夠獨(dú)立 (如果所有元素通過散列函數(shù)計(jì)算都得到相同的位置,造成了平均插入成本的廠長速度遠(yuǎn)遠(yuǎn)高于了負(fù)載系數(shù)的成長速度)
二次探測
- F(i) = i^2,如果計(jì)算得到新元素的位置是H,但是這個(gè)位置已經(jīng)被占用了,將會依序嘗試 H+1^2 H+2^2? H+3^2 等等,而不是H+1 H+2?
- ?如果將表格的大小設(shè)定為質(zhì)數(shù),保持負(fù)載系數(shù)低于0.5,那么沒插入一個(gè)元素所需要的探測次數(shù)不多于 2
開鏈
- 每一個(gè)表格元素維護(hù)一個(gè)list,然后對list進(jìn)行元素的插入 刪除等操作
- hashtable使用開鏈法
hashtable的桶子和節(jié)點(diǎn)
- hashtable表格內(nèi)的元素為桶子 ,名稱的含義是表格內(nèi)的每個(gè)單元涵蓋的不只是個(gè)節(jié)點(diǎn),甚至是一桶節(jié)點(diǎn)
- bucket使用的linked list,不是采用stl源碼中的list slist?,而是自行維護(hù)上述的hash table node
- buckets聚合體則以vector完成,從而具備了擴(kuò)充的能力
hashtable迭代器
- hashable迭代器維持著與整個(gè)buckets vector的關(guān)系,并記錄目前所指的節(jié)點(diǎn)
- 前進(jìn)操作是從目前節(jié)點(diǎn)出發(fā)前進(jìn)一個(gè)位置,由于節(jié)點(diǎn)被安置于list內(nèi),使用next進(jìn)行前進(jìn)操作
- 如果目前是list的尾端,則跳轉(zhuǎn)至下一個(gè)bucket上,正是指向下一個(gè)list的頭部
- 一篇足矣,帶你吃透STL源碼中hash table(哈希表)與關(guān)聯(lián)式容器hash_set、hash_map_董哥的黑板報(bào)-CSDN博客
- hashtable的迭代器沒有后退操作,hashtable沒有定義所謂的逆向迭代器
hashtable的數(shù)據(jù)結(jié)構(gòu)
- buckets聚合體以vector完成,以利動態(tài)擴(kuò)充
- <stl_hash_fun.h>定義數(shù)個(gè)現(xiàn)成的hash functions 全都是仿函數(shù),hash function計(jì)算單元的位置,也就是元素對應(yīng)的bucket的位置,具體調(diào)用的函數(shù)是bkt_num(),它調(diào)用hash function取得一個(gè)可以執(zhí)行modulus(取模)運(yùn)算的數(shù)值
- 按照質(zhì)數(shù)設(shè)計(jì)vector的大小,事先準(zhǔn)備好28個(gè)質(zhì)數(shù),并設(shè)計(jì)一個(gè)函數(shù)用于查詢最接近某數(shù)并大于某數(shù)的質(zhì)數(shù)
hashtable的構(gòu)造和內(nèi)存管理
- vector的reserve的使用(避免內(nèi)存重新分配以及內(nèi)存分配的方式)_Zero's Zone-CSDN博客
- 判斷元素落在哪一個(gè)bucket內(nèi)?這是hash function的任務(wù),但是SGI STL對其進(jìn)行了封裝先交給bkt_num()函數(shù) 再由此函數(shù)調(diào)用hash function,得到一個(gè)可以執(zhí)行的modules(取模)運(yùn)算的數(shù)值
- 以上的目的是出于 有些元素的型別是無法直接對其進(jìn)行取模運(yùn)算的,比如字符串類型?
復(fù)制和整體刪除
- hash table是由vector和linked list組合而成的,因此復(fù)制和整體刪除都需要注意內(nèi)存的釋放的問題
整體代碼
#include <iostream> #include <vector>#ifdef __STL_USE_EXCEPTIONS #define __STL_TRY try #define __STL_UNWIND(action) catch(...) { action; throw; } #else #define __STL_TRY #define __STL_UNWIND(action) #endiftemplate<class T,class Alloc> class simple_alloc{ public:static T* allocate(std::size_t n){return 0==n?0:(T*)Alloc::allocate(n * sizeof(T));}static T* allocate(void){return (T*)Alloc::allocate(sizeof (T));}static void deallocate(T* p,size_t n){if (n!=0){Alloc::deallocate(p,n * sizeof(T));}}static void deallocate(T* p){Alloc::deallocate(p,sizeof(T));} };namespace Chy{template <class T>inline T* _allocate(ptrdiff_t size,T*){std::set_new_handler(0);T* tmp = (T*)(::operator new((std::size_t)(size * sizeof (T))));if (tmp == 0){std::cerr << "out of memory" << std::endl;exit(1);}return tmp;}template<class T>inline void _deallocate(T* buffer){::operator delete (buffer);}template<class T1,class T2>inline void _construct(T1 *p,const T2& value){new(p) T1 (value); //沒看懂}template <class T>inline void _destroy(T* ptr){ptr->~T();}template <class T>class allocator{public:typedef T value_type;typedef T* pointer;typedef const T* const_pointer;typedef T& reference;typedef const T& const_reference;typedef std::size_t size_type;typedef ptrdiff_t difference_type;template<class U>struct rebind{typedef allocator<U>other;};pointer allocate(size_type n,const void * hint = 0){return _allocate((difference_type)n,(pointer)0);}void deallocate(pointer p,size_type n){_deallocate(p);}void construct(pointer p,const T& value){_construct(p,value);}void destroy(pointer p){_destroy(p);}pointer address(reference x){return (pointer)&x;}const_pointer const_address(const_reference x){return (const_pointer)&x;}size_type max_size()const{return size_type(UINT_MAX/sizeof (T));}}; }template <class Value> struct __hashtable_node{__hashtable_node* next;Value val; }; /** Key: 節(jié)點(diǎn)的實(shí)值類型* Value: 節(jié)點(diǎn)的鍵值類型* HashFun: hash function的函數(shù)型別* ExtractKey: 從節(jié)點(diǎn)中提取鍵值的方法 (函數(shù)或者仿函數(shù))* EqualKey: 判斷鍵值是否相同 (函數(shù)或者仿函數(shù))* Alloc: 空間配置器 缺省使用 std::alloc*/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; //為template型別參數(shù)重新定義一個(gè)名稱typedef EqualKey key_equal;//為template型別參數(shù)重新定義一個(gè)名稱typedef std::size_t size_type;typedef ptrdiff_t difference_type;private://以下三者都是function objects//<stl_hash_fun.h> 定義有數(shù)個(gè)標(biāo)準(zhǔn)型別(如 int、c-style、string等)的hasherhasher hash; //散列函數(shù)key_equal equals; //判斷鍵值是否相等ExtractKey get_key; //從節(jié)點(diǎn)取出鍵值typedef __hashtable_node<Value>node;//專屬的節(jié)點(diǎn)配置器typedef simple_alloc<node,Alloc>node_allocator;//節(jié)點(diǎn)的配置函數(shù)node* new_node(const value_type& obj){node* n = node_allocator::allocate();n->next = 0;__STL_TRY{Chy::allocator<Key>::construct(&n->val,obj);return n;};__STL_UNWIND(node_allocator::deallocate(n);)}//節(jié)點(diǎn)釋放函數(shù)void delete_node(node* n){Chy::allocator<Key>::destroy(n->val);node_allocator::deallocate(n);}public:std::vector<node*,Alloc>buckets;//以vector完成桶的集合,其實(shí)值是一個(gè)node*size_type num_elements; //node的個(gè)數(shù) public://bucket個(gè)數(shù) 即buckets vector的大小size_type bucket_count() const{return buckets.size();}//注意假設(shè) 假設(shè)long至少有32bitstatic const int __stl_num_primes = 28;constexpr 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指數(shù)中,最接近并大于n的那個(gè)質(zhì)數(shù)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;const unsigned long *pos = std::lower_bound(first,last,n);//使用lower_bound() 需要先進(jìn)行排序return pos == last ? *(last-1) : *pos;}//總共有多少個(gè)buckets。以下是hash_table的一個(gè)member functionsize_type max_bucket_count()const{//其數(shù)值將為 4294967291return __stl_prime_list[__stl_num_primes - 1];}//構(gòu)造函數(shù)hashtable(size_type n,const HashFcn& hf,const EqualKey& eql):hash(hf),equals(eql),get_key(ExtractKey()),num_elements(0){initialize_buckets(n);}//初始化函數(shù)void initialize_buckets(size_type n){//例子:傳入50 返回53//然后保留53個(gè)元素的空間 然后將其全部填充為0const size_type n_buckets = next_size(n);buckets.reserve(n_buckets);//設(shè)定所有的buckets的初值為0(node*)buckets.insert(buckets.begin(),n_buckets,(node*)0);}public://版本1:接受實(shí)值(value)和buckets個(gè)數(shù)size_type bkt_num(const value_type& obj, size_t n) const{return bkt_num_key(get_key(obj), n); //調(diào)用版本4}//版本2:只接受實(shí)值(value)size_type bkt_num(const value_type& obj) const{return bkt_num_key(get_key(obj)); //調(diào)用版本3}//版本3,只接受鍵值size_type bkt_num_key(const key_type& key) const{return bkt_num_key(key, buckets.size()); //調(diào)用版本4}//版本4:接受鍵值和buckets個(gè)數(shù)size_type bkt_num_key(const key_type& key, size_t n) const{return hash(key) % n; //SGI的所有內(nèi)建的hash(),在后面的hash functions中介紹}public://相關(guān)對應(yīng)的函數(shù)//next_size()返回最接近n并大于n的質(zhì)數(shù)size_type next_size(size_type n) const {return __stl_next_prime(n);}typedef hashtable<Value,Key,HashFcn,ExtractKey,EqualKey,Alloc>iterator;//插入操作和表格重整//插入元素不允許重復(fù)std::pair<iterator,bool>insert_unique(const value_type& obj){//判斷是否需要重建表格 如果需要就進(jìn)行擴(kuò)充resize(num_elements + 1);return insert_unique_noresize(obj);}//函數(shù)判斷是否需要重建表格 如果不需要立刻返回,如果需要 就重建表格void resize(size_type num_elements_hint){//表格重建與否的原則是:元素的個(gè)數(shù)(新增元素計(jì)入之后)和先前分配的bucket vector進(jìn)行比較//如果前者的大于后者 就需要表格的重建//因此 bucket(list)的最大容量和buckets vector的大小相同const size_type old_n = buckets.size();if (old_n < num_elements_hint){//需要重新分配內(nèi)存//計(jì)算下一個(gè)質(zhì)數(shù)const size_type n = next_size(num_elements_hint);if (n > old_n){std::vector<node*,Alloc>tmp(n,(node*)0);__STL_TRY{//處理每一個(gè)舊的bucketfor (size_type bucket=0;bucket<old_n;bucket++) {//指向節(jié)點(diǎn)所對應(yīng)的的串行的起始節(jié)點(diǎn)node* first = buckets[bucket];//處理每一個(gè)舊的bucket所含(串行)的每一個(gè)節(jié)點(diǎn)while(first){//串行節(jié)點(diǎn)還未結(jié)束//找出節(jié)點(diǎn)落在哪一個(gè)新的bucket內(nèi)部size_type new_bucket = bkt_num(first->val,n);//以下四個(gè)操作頗為巧妙//(1)令舊bucket指向其所對應(yīng)的串行的下一個(gè)節(jié)點(diǎn)(以便迭代處理)buckets[bucket] = first->next;//(2)(3)將當(dāng)前節(jié)點(diǎn)插入到新的bucket內(nèi)部,成為其對應(yīng)串行的第一個(gè)節(jié)點(diǎn)first->next = tmp[new_bucket];tmp[new_bucket] = first;//(4)回到舊的bucket所指向的待處理的串行,準(zhǔn)備處理下一個(gè)節(jié)點(diǎn)first = buckets[bucket];}}//對調(diào)新舊兩個(gè)buckets//離開的時(shí)候會釋放tmp的內(nèi)存buckets.swap(tmp);};}}}//在不需要重建表格的情況下插入新的節(jié)點(diǎn) 鍵值不允許重復(fù)std::pair<iterator,bool>insert_unique_noresize(const value_type& obj){const size_type n = bkt_num(obj) ;//決定obj應(yīng)該位于 第n n bucketnode* first = buckets[n]; //令first指向bucket對應(yīng)的串行頭部//如果Buckets[n]已經(jīng)被占用 此時(shí)first不再是0 于是進(jìn)入以下循環(huán)//走過bucket所對應(yīng)的整個(gè)鏈表for (node* cur = first;cur;cur = cur->next) {if (equals(get_key(cur->val)),get_key(obj)){//如果發(fā)現(xiàn)和鏈表中的某個(gè)鍵值是相同的 就不插入 立刻返回return std::pair<iterator,bool>(iterator(cur, this), false);}//離開上述循環(huán)(或者根本沒有進(jìn)入循環(huán)的時(shí)候)first指向bucket的所指鏈表的頭部節(jié)點(diǎn)node* tmp = new_node(obj); //產(chǎn)生新的節(jié)點(diǎn)tmp->next = first;buckets[n] = tmp; //令新的節(jié)點(diǎn)成為鏈表的第一個(gè)節(jié)點(diǎn)++num_elements; //節(jié)點(diǎn)的個(gè)數(shù)累加return std::pair<iterator,bool>(iterator(tmp,this),true);}}//客戶端執(zhí)行的是另外一種節(jié)點(diǎn)的插入行為(不再是insert_unique 而是insert_equal)//插入元素 允許重復(fù)iterator insert_equal(const value_type& obj){//判斷是否需要重建表格 如果需要就進(jìn)行擴(kuò)充resize(num_elements+1);return insert_equal_noresize(obj);}//在不需要重建表格的情況下 插入新的節(jié)點(diǎn),鍵值是允許重復(fù)的iterator insert_equal_noresize(const value_type& obj){const size_type n = bkt_num(obj); //決定obj應(yīng)該位于第 n bucketnode* first = buckets[n];//令first指向的bucket對應(yīng)的鏈表的頭部//如果bucket[n]已經(jīng)被占用,此時(shí)的first不為0,進(jìn)入循環(huán)//遍歷整個(gè)鏈表for(node* cur = first;cur;cur = cur->next){if (equals(get_key(cur->val),get_key(obj))){//如果發(fā)現(xiàn)與鏈表中的某個(gè)鍵值相同,就馬上插入,然后返回node* tmp = new_node(obj); //產(chǎn)生新的節(jié)點(diǎn)tmp->next = cur->next;//新節(jié)點(diǎn)插入目前的位置cur->next = tmp;++num_elements;return iterator (tmp, this); //返回一個(gè)迭代器 指向新增的節(jié)點(diǎn)}//進(jìn)行到這個(gè)時(shí)候 表示沒有發(fā)現(xiàn)重復(fù)的數(shù)值node* tmp = new_node(obj);tmp->next = first;buckets[n] = tmp;++num_elements;return iterator(tmp, this);}}void clear(){//針對每一個(gè)bucketfor(size_type i = 0;i < buckets.size();++i){node * cur = buckets[i];//刪除bucket list中的每一個(gè)節(jié)點(diǎn)while(cur != 0){node* next = cur->next;delete_node(cur);cur = next;}buckets[i] = 0; //令buckets內(nèi)容為null}num_elements = 0; //令總的節(jié)點(diǎn)的個(gè)數(shù)為0//需要注意 buckets vector并沒有釋放空間,仍然保存先前的大小}void copy_from(const hashtable& ht){//先清除己方的buckets vector,此操作是調(diào)用vector::clear() 造成所有的元素都為0buckets.clear();//為己方的buckets vector保留空間,使與對方相同//如果己方的空間大于對方 就不需要改變;如果己方的空間小于對方 就會增大buckets.reserve(ht.buckets.size());//從己方的buckets vector尾端開始,插入n個(gè)元素,其數(shù)值為 null 指針//注意此時(shí)buckets vector為空,所謂的尾端就是起頭處buckets.insert(buckets.end(),ht.buckets.size(),(node*)0);__STL_TRY{//針對buckets vectorfor (size_type i = 0;i<ht.buckets.size();++i) {//復(fù)制vector的每一個(gè)元素(是一個(gè)指針,指向hashtable節(jié)點(diǎn))if (const node* cur = ht.buckets[i]){node* copy = new_node(cur->val);buckets[i] = copy;//針對同一個(gè) buckets list 復(fù)制每一個(gè)節(jié)點(diǎn)for (node* next = cur->next;next ; cur = next,next = cur->next) {copy->next = new_node(next->val);copy = copy->next;}}}//重新登錄的節(jié)點(diǎn)的個(gè)數(shù)(hashtable的大小)num_elements = ht.num_elements;};__STL_UNWIND(clear());}};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 __hash_const 靜態(tài)迭代器typedef __hashtable_node<Value>node;typedef std::forward_iterator_tag iterator_category;typedef Value value_type;typedef ptrdiff_t difference_type;typedef std::size_t size_type;typedef Value& reference;typedef Value* pointer;node* cur;// 迭代器目前所指的節(jié)點(diǎn)hashtable* ht;//保持對容器的連接關(guān)系 (因?yàn)榭赡苄枰獜腷ucket跳到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;} };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; //如果存在 就是他,否則進(jìn)入以下的if流程if (!cur){//根據(jù)元素的數(shù)值,定位出下一個(gè)bucket,其起頭處就是我們的目的地size_type bucket = ht->bkt_num(old->val);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> __hashtable_iterator<V,K,HF,ExK,EqK,A> __hashtable_iterator<V,K,HF,ExK,EqK,A>::operator++(int) {iterator tmp = *this;++this; //調(diào)用operator++return tmp; }問題
- hashtable不能直接被引用,屬于內(nèi)置類型。不被外部使用
- 客戶端可以使用<hash_set.h> 和 <hash_map.h>
- 當(dāng)超過了buckets vector就進(jìn)行表格的重建?
?hash_functions
- 仿函數(shù)
- bkt_num() 調(diào)用此處的hash function得到一個(gè)可以對hashtable進(jìn)行模運(yùn)算的數(shù)值
- 如果是char int long等整數(shù)型別,什么都不做;如果是字符串類型的比如const char* 就需要設(shè)計(jì)一個(gè)轉(zhuǎn)換函數(shù)
?
- 上述代碼表明 SGI hashtable無法處理上述各項(xiàng)型別之外的元素,比如string double float,如果想要處理這些型別 是需要自行定義hash function的
?
#include <iostream> #include <vector>#ifdef __STL_USE_EXCEPTIONS #define __STL_TRY try #define __STL_UNWIND(action) catch(...) { action; throw; } #else #define __STL_TRY #define __STL_UNWIND(action) #endiftemplate<class T,class Alloc> class simple_alloc{ public:static T* allocate(std::size_t n){return 0==n?0:(T*)Alloc::allocate(n * sizeof(T));}static T* allocate(void){return (T*)Alloc::allocate(sizeof (T));}static void deallocate(T* p,size_t n){if (n!=0){Alloc::deallocate(p,n * sizeof(T));}}static void deallocate(T* p){Alloc::deallocate(p,sizeof(T));} };namespace Chy{template <class T>inline T* _allocate(ptrdiff_t size,T*){std::set_new_handler(0);T* tmp = (T*)(::operator new((std::size_t)(size * sizeof (T))));if (tmp == 0){std::cerr << "out of memory" << std::endl;exit(1);}return tmp;}template<class T>inline void _deallocate(T* buffer){::operator delete (buffer);}template<class T1,class T2>inline void _construct(T1 *p,const T2& value){new(p) T1 (value); //沒看懂}template <class T>inline void _destroy(T* ptr){ptr->~T();}template <class T>class allocator{public:typedef T value_type;typedef T* pointer;typedef const T* const_pointer;typedef T& reference;typedef const T& const_reference;typedef std::size_t size_type;typedef ptrdiff_t difference_type;template<class U>struct rebind{typedef allocator<U>other;};pointer allocate(size_type n,const void * hint = 0){return _allocate((difference_type)n,(pointer)0);}void deallocate(pointer p,size_type n){_deallocate(p);}void construct(pointer p,const T& value){_construct(p,value);}void destroy(pointer p){_destroy(p);}pointer address(reference x){return (pointer)&x;}const_pointer const_address(const_reference x){return (const_pointer)&x;}size_type max_size()const{return size_type(UINT_MAX/sizeof (T));}}; }template <class Value> struct __hashtable_node{__hashtable_node* next;Value val; }; /** Key: 節(jié)點(diǎn)的實(shí)值類型* Value: 節(jié)點(diǎn)的鍵值類型* HashFun: hash function的函數(shù)型別* ExtractKey: 從節(jié)點(diǎn)中提取鍵值的方法 (函數(shù)或者仿函數(shù))* EqualKey: 判斷鍵值是否相同 (函數(shù)或者仿函數(shù))* Alloc: 空間配置器 缺省使用 std::alloc*/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; //為template型別參數(shù)重新定義一個(gè)名稱typedef EqualKey key_equal;//為template型別參數(shù)重新定義一個(gè)名稱typedef std::size_t size_type;typedef ptrdiff_t difference_type;private://以下三者都是function objects//<stl_hash_fun.h> 定義有數(shù)個(gè)標(biāo)準(zhǔn)型別(如 int、c-style、string等)的hasherhasher hash; //散列函數(shù)key_equal equals; //判斷鍵值是否相等ExtractKey get_key; //從節(jié)點(diǎn)取出鍵值typedef __hashtable_node<Value>node;//專屬的節(jié)點(diǎn)配置器typedef simple_alloc<node,Alloc>node_allocator;//節(jié)點(diǎn)的配置函數(shù)node* new_node(const value_type& obj){node* n = node_allocator::allocate();n->next = 0;__STL_TRY{Chy::allocator<Key>::construct(&n->val,obj);return n;};__STL_UNWIND(node_allocator::deallocate(n);)}//節(jié)點(diǎn)釋放函數(shù)void delete_node(node* n){Chy::allocator<Key>::destroy(n->val);node_allocator::deallocate(n);}public:std::vector<node*,Alloc>buckets;//以vector完成桶的集合,其實(shí)值是一個(gè)node*size_type num_elements; //node的個(gè)數(shù) public://bucket個(gè)數(shù) 即buckets vector的大小size_type bucket_count() const{return buckets.size();}//注意假設(shè) 假設(shè)long至少有32bitstatic const int __stl_num_primes = 28;constexpr 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指數(shù)中,最接近并大于n的那個(gè)質(zhì)數(shù)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;const unsigned long *pos = std::lower_bound(first,last,n);//使用lower_bound() 需要先進(jìn)行排序return pos == last ? *(last-1) : *pos;}//總共有多少個(gè)buckets。以下是hash_table的一個(gè)member functionsize_type max_bucket_count()const{//其數(shù)值將為 4294967291return __stl_prime_list[__stl_num_primes - 1];}//構(gòu)造函數(shù)hashtable(size_type n,const HashFcn& hf,const EqualKey& eql):hash(hf),equals(eql),get_key(ExtractKey()),num_elements(0){initialize_buckets(n);}//初始化函數(shù)void initialize_buckets(size_type n){//例子:傳入50 返回53//然后保留53個(gè)元素的空間 然后將其全部填充為0const size_type n_buckets = next_size(n);buckets.reserve(n_buckets);//設(shè)定所有的buckets的初值為0(node*)buckets.insert(buckets.begin(),n_buckets,(node*)0);}public://版本1:接受實(shí)值(value)和buckets個(gè)數(shù)size_type bkt_num(const value_type& obj, size_t n) const{return bkt_num_key(get_key(obj), n); //調(diào)用版本4}//版本2:只接受實(shí)值(value)size_type bkt_num(const value_type& obj) const{return bkt_num_key(get_key(obj)); //調(diào)用版本3}//版本3,只接受鍵值size_type bkt_num_key(const key_type& key) const{return bkt_num_key(key, buckets.size()); //調(diào)用版本4}//版本4:接受鍵值和buckets個(gè)數(shù)size_type bkt_num_key(const key_type& key, size_t n) const{return hash(key) % n; //SGI的所有內(nèi)建的hash(),在后面的hash functions中介紹}public://相關(guān)對應(yīng)的函數(shù)//next_size()返回最接近n并大于n的質(zhì)數(shù)size_type next_size(size_type n) const {return __stl_next_prime(n);}typedef hashtable<Value,Key,HashFcn,ExtractKey,EqualKey,Alloc>iterator;//插入操作和表格重整//插入元素不允許重復(fù)std::pair<iterator,bool>insert_unique(const value_type& obj){//判斷是否需要重建表格 如果需要就進(jìn)行擴(kuò)充resize(num_elements + 1);return insert_unique_noresize(obj);}//函數(shù)判斷是否需要重建表格 如果不需要立刻返回,如果需要 就重建表格void resize(size_type num_elements_hint){//表格重建與否的原則是:元素的個(gè)數(shù)(新增元素計(jì)入之后)和先前分配的bucket vector進(jìn)行比較//如果前者的大于后者 就需要表格的重建//因此 bucket(list)的最大容量和buckets vector的大小相同const size_type old_n = buckets.size();if (old_n < num_elements_hint){//需要重新分配內(nèi)存//計(jì)算下一個(gè)質(zhì)數(shù)const size_type n = next_size(num_elements_hint);if (n > old_n){std::vector<node*,Alloc>tmp(n,(node*)0);__STL_TRY{//處理每一個(gè)舊的bucketfor (size_type bucket=0;bucket<old_n;bucket++) {//指向節(jié)點(diǎn)所對應(yīng)的的串行的起始節(jié)點(diǎn)node* first = buckets[bucket];//處理每一個(gè)舊的bucket所含(串行)的每一個(gè)節(jié)點(diǎn)while(first){//串行節(jié)點(diǎn)還未結(jié)束//找出節(jié)點(diǎn)落在哪一個(gè)新的bucket內(nèi)部size_type new_bucket = bkt_num(first->val,n);//以下四個(gè)操作頗為巧妙//(1)令舊bucket指向其所對應(yīng)的串行的下一個(gè)節(jié)點(diǎn)(以便迭代處理)buckets[bucket] = first->next;//(2)(3)將當(dāng)前節(jié)點(diǎn)插入到新的bucket內(nèi)部,成為其對應(yīng)串行的第一個(gè)節(jié)點(diǎn)first->next = tmp[new_bucket];tmp[new_bucket] = first;//(4)回到舊的bucket所指向的待處理的串行,準(zhǔn)備處理下一個(gè)節(jié)點(diǎn)first = buckets[bucket];}}//對調(diào)新舊兩個(gè)buckets//離開的時(shí)候會釋放tmp的內(nèi)存buckets.swap(tmp);};}}}//在不需要重建表格的情況下插入新的節(jié)點(diǎn) 鍵值不允許重復(fù)std::pair<iterator,bool>insert_unique_noresize(const value_type& obj){const size_type n = bkt_num(obj) ;//決定obj應(yīng)該位于 第n n bucketnode* first = buckets[n]; //令first指向bucket對應(yīng)的串行頭部//如果Buckets[n]已經(jīng)被占用 此時(shí)first不再是0 于是進(jìn)入以下循環(huán)//走過bucket所對應(yīng)的整個(gè)鏈表for (node* cur = first;cur;cur = cur->next) {if (equals(get_key(cur->val)),get_key(obj)){//如果發(fā)現(xiàn)和鏈表中的某個(gè)鍵值是相同的 就不插入 立刻返回return std::pair<iterator,bool>(iterator(cur, this), false);}//離開上述循環(huán)(或者根本沒有進(jìn)入循環(huán)的時(shí)候)first指向bucket的所指鏈表的頭部節(jié)點(diǎn)node* tmp = new_node(obj); //產(chǎn)生新的節(jié)點(diǎn)tmp->next = first;buckets[n] = tmp; //令新的節(jié)點(diǎn)成為鏈表的第一個(gè)節(jié)點(diǎn)++num_elements; //節(jié)點(diǎn)的個(gè)數(shù)累加return std::pair<iterator,bool>(iterator(tmp,this),true);}}//客戶端執(zhí)行的是另外一種節(jié)點(diǎn)的插入行為(不再是insert_unique 而是insert_equal)//插入元素 允許重復(fù)iterator insert_equal(const value_type& obj){//判斷是否需要重建表格 如果需要就進(jìn)行擴(kuò)充resize(num_elements+1);return insert_equal_noresize(obj);}//在不需要重建表格的情況下 插入新的節(jié)點(diǎn),鍵值是允許重復(fù)的iterator insert_equal_noresize(const value_type& obj){const size_type n = bkt_num(obj); //決定obj應(yīng)該位于第 n bucketnode* first = buckets[n];//令first指向的bucket對應(yīng)的鏈表的頭部//如果bucket[n]已經(jīng)被占用,此時(shí)的first不為0,進(jìn)入循環(huán)//遍歷整個(gè)鏈表for(node* cur = first;cur;cur = cur->next){if (equals(get_key(cur->val),get_key(obj))){//如果發(fā)現(xiàn)與鏈表中的某個(gè)鍵值相同,就馬上插入,然后返回node* tmp = new_node(obj); //產(chǎn)生新的節(jié)點(diǎn)tmp->next = cur->next;//新節(jié)點(diǎn)插入目前的位置cur->next = tmp;++num_elements;return iterator (tmp, this); //返回一個(gè)迭代器 指向新增的節(jié)點(diǎn)}//進(jìn)行到這個(gè)時(shí)候 表示沒有發(fā)現(xiàn)重復(fù)的數(shù)值node* tmp = new_node(obj);tmp->next = first;buckets[n] = tmp;++num_elements;return iterator(tmp, this);}}void clear(){//針對每一個(gè)bucketfor(size_type i = 0;i < buckets.size();++i){node * cur = buckets[i];//刪除bucket list中的每一個(gè)節(jié)點(diǎn)while(cur != 0){node* next = cur->next;delete_node(cur);cur = next;}buckets[i] = 0; //令buckets內(nèi)容為null}num_elements = 0; //令總的節(jié)點(diǎn)的個(gè)數(shù)為0//需要注意 buckets vector并沒有釋放空間,仍然保存先前的大小}void copy_from(const hashtable& ht){//先清除己方的buckets vector,此操作是調(diào)用vector::clear() 造成所有的元素都為0buckets.clear();//為己方的buckets vector保留空間,使與對方相同//如果己方的空間大于對方 就不需要改變;如果己方的空間小于對方 就會增大buckets.reserve(ht.buckets.size());//從己方的buckets vector尾端開始,插入n個(gè)元素,其數(shù)值為 null 指針//注意此時(shí)buckets vector為空,所謂的尾端就是起頭處buckets.insert(buckets.end(),ht.buckets.size(),(node*)0);__STL_TRY{//針對buckets vectorfor (size_type i = 0;i<ht.buckets.size();++i) {//復(fù)制vector的每一個(gè)元素(是一個(gè)指針,指向hashtable節(jié)點(diǎn))if (const node* cur = ht.buckets[i]){node* copy = new_node(cur->val);buckets[i] = copy;//針對同一個(gè) buckets list 復(fù)制每一個(gè)節(jié)點(diǎn)for (node* next = cur->next;next ; cur = next,next = cur->next) {copy->next = new_node(next->val);copy = copy->next;}}}//重新登錄的節(jié)點(diǎn)的個(gè)數(shù)(hashtable的大小)num_elements = ht.num_elements;};__STL_UNWIND(clear());}//元素查找iterator find(const key_type& key){size_type n = bkt_num(key); //首先尋找落在哪一個(gè)bucket里面node* first;//以下 從bucket list的頭部開始,逐一比對每個(gè)元素的數(shù)值,比對成功就退出for (first = buckets[n];first && !equals(get_key(first->val),key);first = first->next) {}return iterator (first,this);}//元素計(jì)數(shù)size_type count (const key_type& key)const{const size_type n = bkt_num_key(key);//首先尋找落在哪一個(gè)bucket里面size_type result = 0;//遍歷bucket list,從頭部開始,逐一比對每個(gè)元素的數(shù)值。比對成功就累加1for(const node* cur = buckets[n];cur;cur = cur->next){if (equals(get_key(cur->val),key)){++result;}}return result;}};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 __hash_const 靜態(tài)迭代器typedef __hashtable_node<Value>node;typedef std::forward_iterator_tag iterator_category;typedef Value value_type;typedef ptrdiff_t difference_type;typedef std::size_t size_type;typedef Value& reference;typedef Value* pointer;node* cur;// 迭代器目前所指的節(jié)點(diǎn)hashtable* ht;//保持對容器的連接關(guān)系 (因?yàn)榭赡苄枰獜腷ucket跳到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;} };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; //如果存在 就是他,否則進(jìn)入以下的if流程if (!cur){//根據(jù)元素的數(shù)值,定位出下一個(gè)bucket,其起頭處就是我們的目的地size_type bucket = ht->bkt_num(old->val);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> __hashtable_iterator<V,K,HF,ExK,EqK,A> __hashtable_iterator<V,K,HF,ExK,EqK,A>::operator++(int) {iterator tmp = *this;++this; //調(diào)用operator++return tmp; }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 std::size_t (h); }//下面所有的 __STL_TEMPLATE_NULL 在<stl_config.h>里面全部被定義為template<>int main(){const char *input_string("Hello");std::cout << input_string << std::endl;std::cout << __stl_hash_string(input_string) << std::endl; }參考鏈接
- 關(guān)聯(lián)容器 — hashtable · STL源碼分析 · 看云
總結(jié)
以上是生活随笔為你收集整理的STL源码剖析 hashtable的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 如何使用mysql添加更新_Mysql
- 下一篇: STL源码剖析 set相关算法