最近在mfc中用到字典,自己不会在网上查了资料。简单总结一下:
一,CMap是什么?
映射(Map),又称为字典(Dictionary),是由关键字(Key)及其对应的元素值(Value)所组成的元素单
元(Element)的表单式集合。CMap是一个mfc的模板类,可以建立一个从任意类型的变量到另外一个任意类型的
变量的映射(map),用的是哈希表作存储,因此速度较快。对于要求查找速度快一般用数组,对于增加/删除操作
方便的都用链表,但要是两者综合一下,最好还是用合希表。
二,要注意的几个地方:
1.如何声明CMap
许多人对Cmap的声明模式CMap<KEY,ARG_KEY,VALUE,ARG_VALUE>感到迷惑,为什么不用CMap<KEY,VALUE>
呢?实际上,CMap中的的数据最终会是CPair,而CPair内部是(KEY,VALUE)。因此,CMap其实存储的是KEY
,而非ARG_KEY。然而,如果你查看MFC的源代码,几乎CMap所有的内部参数传递都是访问ARG_KEY和ARG_VALUE,因此,使用KEY&来代替ARG_KEY似乎是正确的,除了在这些情况下:
1 应用简单的数据类型,如int ,char用值传递与参数传递没有什么不同
2 如果用CString作为KEY,你应该用LPCTSTR做ARG_KEY而非CString&。
2.有哪些与Map相关的典型操:
1 向Map中插入具有给定关键字的元素单元。
2 在Map中查找具有给定关键字的元素单元。
3 在Map中删除具有给定关键字的元素单元。
4 枚举(遍历)Map中的所有元素单元。
三,简单的例子:
例子一: 我们来看一个CMap的用法,下面示例代码:
CMap<int,int&,CPoint,CPoint&> 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";CMap<int,int&,CPoint,CPoint&> 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";
CMap是个很不错的数据结构,尤其在你建立一个字典的时候。比如idcountry的含义是"中国",这就是一个元组
,也就是一个Pair,Key是"idcountry",而Value是"中国"。
例子二:
1、定义一个CMAP,向这个CMAP中增加数据项(键-值对)。
CMap<CString, LPCTSTR, CString, LPCTSTR>m_ItemMap;
CString strKey = _T(""), str = _T("");
int i;
for(i = 0; i < 5; i++)
{
strKey.Format("%d", i); //这个是键
str.Format("A%d", i); //键对应的值
m_ItemMap.SetAt(strKey, str);
}
2、遍历正个CMAP的常用方法。
POSITION pos = m_ItemMap.GetStartPosition();
while(pos)
{
m_ItemMap.GetNextAssoc(pos, strKey, str);
cout<< strKey<< ":"<< str<< endl;
}
3、在CMAP中查找相应的数据项。
CString pReset;
if(m_ItemMap.Lookup("1", pReset))
{
cout<<pReset<<endl;
}
cmap用法,很详细(转)
土戈最新推荐文章于 2021-12-06 11:12:57 发布2071 收藏 1 版权一、 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是否为空(无元素单元) |
应用实例如下:
MyMap.InitHashTable(257); |
2、选择适当大小的奇数-- 或者,有可能的话,使用素数的效果会更好一些--来作为哈希表的初始值。
3、然后,向myMap中添加元素单元。
4、使用myMap进行数据映射、查找、遍历等操作。
5、调用myMap.RemoveAll()函数移除所有元素,释放myMap占用的内存空间。
CMap对应IMPLEMENT_SERIAL,从而支持用户对其元素进行串行化(Serialization)以及倾注(Dumping)操作。在 对CMap的独立元素进行倾注操作时,应该注意的是,你必须将倾注环境(Dump Context)的深度设置为1或者更大的数字。