說來慚愧,使用了很久visual stdio 2003了,只知道mfc公升級到了7.0,atl也公升級到了7.0,對於這兩個經典的類庫做了一些研究,但一直沒有注意c++標準庫的變化。
今天嘗試的使用了stdext::hash_map這個庫,果然不錯。下面寫下一些心得。
hash_map類在標頭檔案hash_map中,和所有其它的c++標準庫一樣,標頭檔案沒有副檔名。如下宣告:
#include
using namespace std;
using namespace stdext;
hash_map是乙個聚合類,它繼承自_hash類,包括乙個vector,乙個list和乙個pair,其中vector用於儲存桶,list用於進行衝突處理,pair用於儲存key->value結構,簡要地偽碼如下:
class hash_map;
當然,這只是乙個簡單模型,c++標準庫的泛型模版一向以巢狀複雜而聞名,初學時看類庫,無疑天書啊。微軟的hash_map類還聚合了hash_compare仿函式類,hash_compare類裡有聚合了less仿函式類,亂七八糟的。
下面說說使用方法:
一、簡單變數作為索引:整形、實性、指標型
其實指標型也就是整形,演算法一樣。但是hash_map會對char*, const char*, wchar_t*, const wchar_t*做特殊處理。
這種情況最簡單,下面**是整形示例:
hash_mapinthash;
inthash[1] = 123;
inthash[2] = 456;
int val = inthash[1];
int val = inthash[2];
實型和指標型用法和整形一樣,原理如下:
1、使用簡單型別作索引宣告hash_map的時候,不需要宣告模版的後兩個引數(最後乙個引數指名hash_map節點的儲存方式,預設為pair,我覺得這就挺好,沒必要修改),使用預設值就好。
2、對於除過字串的其它簡單型別,hash_map使用模版函式 size_t hash_value(const _kty& _keyval) 計算hash值,計算方法是經典的掩碼異或法,自動溢位得到索引hash值。微軟的工程師也許開了乙個玩笑,這個掩碼被定義為0xdeadbeef(死牛肉,抑或是某個程式設計師的外號)。
3、對於字串指標作索引的時候,使用定型別函式inline size_t hash_value(const char *_str)或inline size_t hash_value(const wchar_t *_str)計算hash值,計算方法是取出每乙個字元求和,自動溢位得到hash值。對於字串型的hash索引,要注意需要自定義less仿函式。
因為我們有理由認為,人們使用hash表進行快速查詢的預期成本要比在hash表中插入的預期成本低得多,所以插入可以比查詢昂貴些;基於這個假設,hash_map在有衝突時,插入鍊錶是進行排序插入的,這樣在進行查詢衝突解決的時候就能夠更快捷的找到需要的索引。
但是,基於泛型程式設計的原則,hash_map也有理由認為每一種型別都支援使用"<"來判別兩個型別值的大小,這種設計恰好讓字串型別無所適從,眾所周知,兩個字串指標的大小並不代表字串值的大小。見如下**:
hash_mapcharhash;
charhash["a"] = 123;
charhash["b"] = 456;
char szinput[64] = "";
scanf("%s", szinput);
int val = charhash[szinput];
最終的結果就是無論輸入任何字串,都無法找到對應的整數值。因為輸入的字串指標是szinput指標,和"a"或"b"字串常量指標的大小是絕對不會相同。解決方法如下:
首先寫乙個仿函式charless,繼承自仿函式基類binary_function(當然也可以不繼承,這樣寫只是符合標準,而且寫起來比較方便,不用被類似於指標的指標和指標的引用搞暈。
struct charless : public binary_function};
很好,有了這個仿函式,就可以正確的使用字串指標型hash_map了。如下:
hash_map> charhash;
charhash["a"] = 123;
charhash["b"] = 456;
char szinput[64] = "";
scanf("%s", szinput);
int val = charhash[szinput];
現在就可以正常工作了。至此,簡單型別的使用方法介紹完畢。
二、使用者自定義型別:比如物件型別,結構體。
這種情況比價複雜,我們先說簡單的,對於c++標準庫的string類。
慶幸的是,微軟為basic_string(string類的基類)提供了hash方法,這使得使用string物件做索引簡單了許多。值得注意(也值得鬱悶)的是,雖然支援string的hash,string類卻沒有過載比較運算子,所以標準的hash_compare仿函式依舊無法工作。我們繼續重寫less仿函式。
struct string_less : public binary_function };
好了,我們可以書寫如下**:
hash_map> stringhash;
stringhash["a"] = 123;
stringhash["b"] = 456;
string strkey = "a";
int val = charhash[strkey];
這樣就可以了。
對於另外的乙個常用的字串類cstring(我認為微軟的cstring比標準庫的string設計要灑脫一些)更加複雜一些。很顯然,標準庫里不包含對於cstring的支援,但cstring卻過載了比較運算子(鬱悶)。我們必須重寫hash_compare仿函式。值得一提的是,在virtual stdio 2003中,cstring不再是mfc的成員,而成為atl的成員,使用#include 就可以使用。我沒有採用重寫hash_compare仿函式的策略,而僅僅是繼承了它,在模版庫中的繼承是沒有效能損耗的,而且能讓我偷一點懶。
首先重寫乙個hash_value函式:
inline size_t cstring_hash_value(const cstring& str) }
return(value);
}其次重寫hash_compare仿函式:
class cstring_hash_compare : public hash_compare
bool operator()(const cstring& _keyval1, const cstring& _keyval2) const };
上面的過載忽略了基類對於less仿函式的引入,因為cstring具備比較運算子,我們可以使用預設的less仿函式,在這裡對映為comp。好了,我們可以宣告新的hash_map物件如下:
hash_mapcstringhash;
其餘的操作一樣一樣的。
下來就說說對於自定義物件的使用方法:首先定義
struct ihashable
;讓我們自寫的類都派生自這裡,有乙個標準,接下來定義我們的類:
class ctest : public ihashable
ctest(const ctest& obj)
public:
virtual ihashable& operator = (const ihashable& val)
virtual unsigned long hash_value() const
virtual bool operator < (const ihashable& val) const };
用這個類的物件做為hash索引準備工作如下,因為介面中規定了比較運算子,所以這裡可以使用標準的less仿函式,所以這裡忽略:
template
class myhashcompare : public hash_compare<_tkey>
bool operator()(const _tkey& _keyval1, const _tkey& _keyval2) const };
下來就這樣寫:
ctest test;
test.m_value = 123;
test.m_message = "this is a test";
myhash[test] = 2005;
int val = myhash[test];
可以看到正確的數字被返回。
三、關於hash_map的思考:
1、效能分析:採用了內聯**和模版技術的hash_map在效率上應該是非常優秀的,但我們還需要注意如下幾點:
* 經過檢視**,字串索引會比簡單型別索引速度慢,自定義型別索引的效能則和我們選擇hash的內容有很大關係,簡單為主,這是使用hash_map的基本原則。
* 可以通過重寫hash_compair仿函式,更改裡面關於桶數量的定義,如果取值合適,也可以得到更優的效能。如果桶數量大於10,則牢記它應該是乙個質數。
* 在自定義型別是,過載的等號(或者拷貝構造)有可能成為效能瓶頸,使用物件指標最為索引將是乙個好的想法,但這就必須重寫less仿函式,理由同使用字串指標作為索引。
使用STL的hash map要點
使用了很久visual stdio 2003了,只知道mfc公升級到了7.0,atl也公升級到了7.0,對於這兩個經典的類庫做了一些研究,但一直沒有注意c 標準庫的變化。今天嘗試的使用了stdext hash map這個庫,果然不錯。下面寫下一些心得。hash map類在標頭檔案hash map中,...
HashMap的幾個要點
1 資料儲存的底層資料結構 2 擴容機制 與rehash 3 同步問題 hashmap hashtable concurrenthashmap的區別與理解 1 hashmap底層是通過陣列 鍊錶的資料結構實現的。2 整體來看hashmap中所有資料都存於乙個陣列table中,陣列中的每個元素又是乙個...
STL 容器要點
1.典型內部結構 dynamic array 2.可隨機訪問 是 3.元素搜尋速度 慢 4.快速安插移除 尾端 5.移除所有值為value的元素 col.erase remove col.begin col.end value col.end deque 1.典型內部結構 array or arra...