cmap用法,很详细(转)

一、 Map的基本知识

  MFC中的提供了基于模板的CMap类。利用CMap模板类,可以处理特定的数据类型,例如用户自定义的类或结构体等。同时,MFC也提供了基于指定 数据类型的非模板类,其中包括: 
 

类名 关键字类型 元素值类型
CMapWordToPtr WORDS Void pointers
CMapPtrToWord Void pointers WORDS
CMapPtrToPtr Void pointers Void pointers
CMapWordToOb WORDS Objects
CMapStringToOb Strings Objects
CMapStringToPtr Strings Void pointers
CMapStringToString Strings String

二、 Map的工作原理 

在MFC的CMap及其相关的Map类中,只要对Map进行正确设置,Lookup函数通常能够一次到位的查找到任意元素,而很少需要进行两次或者三 次以上的查找比对。 

struct CAssoc
{
CAssoc* pNext;
UINT nHashValue;
CString key;
CString value;
};

 

nHashTableSize是哈希表中元素的数目(默认情况下,哈希表的大 小为17)

如果在哈希表中的索引值为i的位置已经容纳了一个CAssoc指针,那么MFC将建立一个单独的CAssoc结构体的链表(List),链表中的第一 个CAssoc结构体的地址被存储到哈希表中,而将第二个CAssoc结构体的地址存储到前一个CAssoc结构体的pNext域,以此类推。

  但是,正如我们先前所讨论的那样,只要正确设置Map,链表中的元素一般就不会超过三个,这就意味着,查找通常可 以在三次元素比对操作之内完成。

三、 优化查找效率

  微软推荐将哈希表的大小设置为Map中所存储元素数目的 110% ~120%,以使得Map的应用性能在内存消耗和查找效率之间取得相对平衡。

在MFC中,指定哈希表大小,可调用InitHashTable()函数:

map.InitHashTable(1200);

从统计学上考虑,用奇数作为哈希表的大小也将有助于减少冲突的发生。因此,初始化一个存储1000个元素的哈希表的InitHashTable() 函数可以如下形式使用:

map.InitHashTable(1201);

同时,在InitHashTable()函数的调用时机上,应该注意的是,该函数应当在map包含有任何元素之前使。如果map中已经包含了一个或者 更多的元素,那么,重新改变map的大小,将会引发断言(Assertion)错误。

尽管MFC中所使用的哈希算法能够适应于大多数场合,但如果您真的有所需要,或者,只要你愿意,用户也可以使用自己的算法来取代原有的算法。对于一个 输入的关键字的值,要计算出它的哈希值,MFC通常调用一个全局模板函数HashKey(),对于大多数数据类型而言,HashKey()函数是以下面的 方式实现的

 


AFX_INLINE UINT AFXAPI HashKey(ARG_KEY key)
{
//一般情况的默认算法。
return ((UINT)(void*)(DWORD)key) >> 4;
}

但对于字符串而言,其具体的实现方式如下:

UINT AFXAPI HashKey(LPCWSTR key) // Unicode 编码字符串
{
UINT nHash = 0;
while (*key)
nHash = (nHash<<5) + nHash + *key++;
return nHash;
}

UINT AFXAPI HashKey(LPCSTR key) // ANSI编码字符串
{
UINT nHash = 0;
while (*key)
nHash = (nHash<<5) + nHash + *key++;
return nHash;
}


要实现对应于特定数据类型的用户自定义哈希算法,您可以使用上述的字符串版本的HashKey()函数作为参考,写一个类似的特定类型的 HashKey()函数。

 

四、 使用MFC中的CMap类

  构造函数:

CMap 构造一个关键字和元素值映射的集合类。

操作: 

Lookup 通过给定的关键字查找相应的元素值。
SetAt 向Map中插入一个元素单元;若存在匹配键字,则替代之。
operator [] 向Map中插入一个元素 -SetAt的子操作
RemoveKey 移除由关键字标示的元素单元
RemoveAll 移除Map中的所有元素单元
GetStartPosition 返回第一个元素单元的位置
GetNextAssoc 读取下一个元素单元
GetHashTableSize 返回哈希表的大小(元素单元的数目)
InitHashTable 初始化哈希表,并指定它的大小

状态: 

GetCount 返回Map中元素的数目
IsEmpty 检查Map是否为空(无元素单元)

应用实例如下: 


CMap myMap;
//初始化哈希表,并指定其大小(取奇数)

MyMap.InitHashTable(257);

//向myMap中添加元素单元。
for (int i=0;i < 200;i++)
myMap.SetAt( i, CPoint(i, i) );

// 删除实际值为偶数的关键字所对应的的元素单元。
POSITION pos = myMap.GetStartPosition();
int nKey;
CPoint pt;
while (pos != NULL)
{
myMap.GetNextAssoc( pos, nKey, pt );

if ((nKey%2) == 0)
myMap.RemoveKey( nKey );
}

#ifdef _DEBUG
afxDump.SetDepth( 1 );
afxDump << "myMap: " << &myMap << "/n";
#endif


2、选择适当大小的奇数-- 或者,有可能的话,使用素数的效果会更好一些--来作为哈希表的初始值。 

3、然后,向myMap中添加元素单元。 

4、使用myMap进行数据映射、查找、遍历等操作。 

5、调用myMap.RemoveAll()函数移除所有元素,释放myMap占用的内存空间。 

CMap对应IMPLEMENT_SERIAL,从而支持用户对其元素进行串行化(Serialization)以及倾注(Dumping)操作。在 对CMap的独立元素进行倾注操作时,应该注意的是,你必须将倾注环境(Dump Context)的深度设置为1或者更大的数字。 

你可能感兴趣的:(cmap用法,很详细(转))