1.CMap定义
template< class KEY, class ARG_KEY, class VALUE, class ARG_VALUE >class CMap : public CObject
参数说明
KEY
key的类型。其类型可以是用户自定义的类
ARG _ KEY
KEY的数据类型。通常是KEY的引用
VALUE
值类型。可以是用户自定义的类
ARG _ VALUE
VALUE的数据类型。通过是VALUE的引用。
2.CMap数据结构
CMap数据结构见下图
数据类型及CMap数据成员
// CPair struct CPair { const KEY key; //Key是类型 VALUE value; protected: //ARG_KEY 是Key的值类型 若Key是CString 则AGR_KEY可以是const char * //ARG_KEY指定的key作为哈希函数的参数 CPair( ARG_KEY keyval ) : key( keyval ) {} } //CAssoc 链表节点 class CAssoc : public CPair { friend class CMap<KEY,ARG_KEY,VALUE,ARG_VALUE>; CAssoc* pNext; //指向下一个链表节点 UINT nHashValue; //哈希值 // needed for efficient iteration public: CAssoc( ARG_KEY key ) : CPair( key ) {} };如果 hash 地址冲突,则插入的元素保存在 pNext 中。
CAssoc** m_pHashTable; //以链表节点代指哈希名 m_pHashTable为CAssoc指针一维数组
UINT m_nHashTableSize; //哈希表容量
INT_PTR m_nCount; //哈希表中元素个数
3.初始化
template<class KEY, class ARG_KEY, class VALUE, class ARG_VALUE> void CMap<KEY, ARG_KEY, VALUE, ARG_VALUE>::InitHashTable( UINT nHashSize, BOOL bAllocNow) { //...... //...... if (bAllocNow) { m_pHashTable = new CAssoc* [nHashSize]; //nHashSize哈希表容器大小 ENSURE(m_pHashTable != NULL); memset(m_pHashTable, 0, sizeof(CAssoc*) * nHashSize); //hash表初始化为0 } m_nHashTableSize = nHashSize; }
4.哈希函数
ARG_KEY用于计算哈希地址
template<class ARG_KEY> AFX_INLINE UINT AFXAPI HashKey(ARG_KEY key) { // default identity hash - works for most primitive values return (DWORD)(((DWORD_PTR)key)>>4); } template<> UINT AFXAPI HashKey<LPCWSTR> (LPCWSTR key); template<> UINT AFXAPI HashKey<LPCSTR> (LPCSTR key); template<> UINT AFXAPI HashKey<LPCWSTR> (LPCWSTR key) { ENSURE_ARG(AfxIsValidString(key)); UINT nHash = 0; while (*key) nHash = (nHash<<5) + nHash + *key++; return nHash; } template<> UINT AFXAPI HashKey<LPCSTR> (LPCSTR key) { ENSURE_ARG(AfxIsValidString(key)); UINT nHash = 0; while (*key) nHash = (nHash<<5) + nHash + *key++; return nHash; }如果 Key 为用户自定义类类型,则必须要提供 Hask 函数。
5.插入一个元素
template<class KEY, class ARG_KEY, class VALUE, class ARG_VALUE> VALUE& CMap<KEY, ARG_KEY, VALUE, ARG_VALUE>::operator[](ARG_KEY key) { ASSERT_VALID(this); UINT nHashBucket, nHashValue; CAssoc* pAssoc; //GetAssocAt 找出key对应的CAssoc节点 //key传入变量 //nHashBucket为传出变量。它为key对应的桶号 即pHashTable数组的索引 //nHashValue为传出变量。它为key对应的值 if ((pAssoc = GetAssocAt(key, nHashBucket, nHashValue)) == NULL) { //...... // it doesn't exist, add a new Association pAssoc = NewAssoc(key); //保存计算得到的哈希值 pAssoc->nHashValue = nHashValue; //'pAssoc->value' is a constructed object, nothing more // put into hash table pAssoc->pNext = m_pHashTable[nHashBucket]; m_pHashTable[nHashBucket] = pAssoc; //创 } //注意,返回的是值的引用。这样可以方便修改key对应的值 //如果value的类型为CPoint 则map[1]=CPoint(562,963); //即pAssoc->value=CPoint(562,963); return pAssoc->value; // return new reference } template<class KEY, class ARG_KEY, class VALUE, class ARG_VALUE> typename CMap<KEY, ARG_KEY, VALUE, ARG_VALUE>::CAssoc* CMap<KEY, ARG_KEY, VALUE, ARG_VALUE>::GetAssocAt(ARG_KEY key, UINT& nHashBucket, UINT& nHashValue) const // find association (or return NULL) { nHashValue = HashKey<ARG_KEY>(key); //计算出哈希值 nHashBucket = nHashValue % m_nHashTableSize; //计算出桶号 if (m_pHashTable == NULL) return NULL; // see if it exists CAssoc* pAssoc; for (pAssoc = m_pHashTable[nHashBucket]; pAssoc != NULL; pAssoc = pAssoc->pNext) { //CompareElements比较键值 处理hash地址冲突 if (pAssoc->nHashValue == nHashValue && CompareElements(&pAssoc->key, &key)) return pAssoc; } return NULL; }
6.查找函数
template<class KEY, class ARG_KEY, class VALUE, class ARG_VALUE> BOOL CMap<KEY, ARG_KEY, VALUE, ARG_VALUE>::Lookup(ARG_KEY key, VALUE& rValue) const { ASSERT_VALID(this); UINT nHashBucket, nHashValue; CAssoc* pAssoc = GetAssocAt(key, nHashBucket, nHashValue); if (pAssoc == NULL) return FALSE; // not in map rValue = pAssoc->value; return TRUE; }
7.实例
#include "stdafx.h" void TestCMap1() { CMap<int,int,CPoint,CPoint> myMap; int i; myMap.InitHashTable( 257 ); // Add 10 elements to the map. for (i=0;i < 200;i++) myMap[i] = CPoint(i, i); // Remove the elements with even key values. CPoint pt; for (i=0; myMap.Lookup( i, pt ) ;i+=2) { myMap.RemoveKey( i ); } #ifdef _DEBUG ASSERT(myMap.GetCount() == 100); afxDump.SetDepth( 1 ); afxDump << "myMap: " << &myMap << "\n"; #endif } void TestCMap2() { CMap<CString, LPCTSTR, CString, LPCTSTR> itemMap; itemMap.InitHashTable(120); itemMap[L"vk"]=L"vi kong"; }