http://hi.baidu.com/wei83523408/blog/item/878ebd3b8898d5e115cecb2b.html

一、 Map的基本知识

  映射(Map),又称为字典(Dictionary),是由关键字(Key)及其对应的元素值(Value)所组成的元素单元 (Element)的表单式集合。

通常,对于Map而言,使用给定的Key,可以迅速地从单元集合中检索到相应的元素。因此,在需要对大量数据进行查找操作而查找的性能又占据重要地位 的场合,Map无疑是一种较理想的容器。譬如,在MFC中,使用Map来实现HandleMaps(句柄映射),以及其他的一些内部数据结构。同 时,MFC也提供了公共Map类。使用公共Map类,MFC程序员可以轻易地高效地根据自身的需求实现程序中自定义的映射。

通常,当一个Map对象被删除时,或者,当其中的元素被移除时,关键字和元素值也将被完全删除。

从数据结构的角度分析,有关Map的典型操作有:

1、向Map中插入具有给定关键字的元素单元。

2、在Map中查找具有给定关键字的元素单元。

3、在Map中删除具有给定关键字的元素单元。

4、枚举(遍历)Map中的所有元素单元。

MFC中的各种Map实现,都提供了实现上述操作的成员函数。为了方便讨论,我们以CMap为代表,进行讲解。

一旦你已经向Map中插入了一个关键字-元素值组合对(Key-Value pair)单元,就可以利用关键字访问Map,从而有效地检索、添加或者删除元素单元,也可以遍历Map中的所有单元。

对MFC中的CMap等,除了关键字访问方法之外,还有另一种不同的类型--POSITION,也可以作为访问元素单元的辅助方式,可以使用一个 POSITION来"记住"一个元素单元或者对Map进行枚举操作。你可能认为这种使用POSITION实现的遍历等同于使用关键字来进行的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的工作原理

使用Map的最大优势是它的快速查找的优秀性能,而取得最优性能的关键在于尽量使得在检索周期内所需进行的元素检查(比对)次数达到最少。顺序查找的 性能是最差的,因为如果使用顺序查找算法在包含n个元素单元的Map中查找某个元素,可能(最坏情况下)需要进行n次独立的比较运算。

二元查找(折中查找)的性能表现要稍好一些,可是,一个不容忽视的问题是--二元查找要求待查序列为有序排列,这无疑会降低Map自身的操作灵活性。 在我们的理解中,所谓的最佳算法应当是不论元素单元数目的多少,也不论元素是以什么顺序进行排列,查找过程都无需任何额外的比对操作,而能够仅仅通过简单 的计算方法,就可以直接指向最终的相应元素的快速、高效算法。这听起来有些玄乎,但事实上,这种算法的确是有可能实现的(而且,我相信,Map可以做得 到)。

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

那么,这种高效的查找是如何实现的呢?

不失一般性,以MFC中的CMap模板类为例。在Map被创建之后(通常是恰恰在第一个元素被插入之前的瞬间),系统会为一个指向CAssoc结构体 的指针数组的哈希表分配内存。MFC使用CAssoc结构体描述元素值和关键字的组合对。

CAssoc结构体描述如下:

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

无论何时,只要有一个元素值-关键字单元被加入到Map中,就会随之创建一个新的CAssoc结构体,并根据单元中的关键字的实际值来计算出相应的哈希 值。同时,拷贝一个指向CAssoc结构体的指针并将其插入到哈希表中索引值为i的位置中。其中,i的计算公式如下:

i=nHashValue%nHushTableSize

式中,nHashValue是由关键字Key的实际值计算出来的哈希值;nHashTableSize是哈希表中元素的数目(默认情况下,哈希表的大 小为17)。

如果在哈希表中的索引值为i的位置已经容纳了一个CAssoc指针,那么MFC将建立一个单独的CAssoc结构体的链表(List),链表中的第一 个CAssoc结构体的地址被存储到哈希表中,而将第二个CAssoc结构体的地址存储到前一个CAssoc结构体的pNext域,以此类推。下图展示了 哈希表的一种可能实现情况,在该哈希表中,共有10个元素,其中5个元素地址分别唯一的存储,另外5个分别存储在长度为2和3的两个链表中。

  调用一个Map的Lookup()函数时,MFC根据输入的关键字的实际值计算相应的哈希值,然后使用前面提到的公式将哈希值转换为索引值,并 从哈希表中的相应位置检索CAssoc指针。

理想情况下,该位置只包含一个CAssoc指针,而非CAssoc指针链表。如果事实情况真如同我们所期望的那样,单一地址对应单一CAssoc指 针,那么,元素单元将能够被一次查找到位,并直接读出;如果从哈希表中检索到的是CAssoc链表的指针头地址,则MFC顺序比对链表元素CAssoc结 构所包含的关键字,直至查找到正确结果。但是,正如我们先前所讨论的那样,只要正确设置Map,链表中的元素一般就不会超过三个,这就意味着,查找通常可 以在三次元素比对操作之内完成。

三、 优化查找效率

  在MFC的Map中,查找性能主要依赖于两个因素:

1、哈希表的大小

2、尽可能产生唯一哈希值的优异算法

哈希表的大小对于Map的查找性能而言,是非常重要的。举个简单的例子,如果有一个Map要容纳1000个元素单元,但是哈希表却只能提供17个存放 CAssoc指针的空间,那么,即使是最佳情况,哈希表中的每个CAssoc链表中也将包含58或59个CAssoc结构体,自然,在这种情况下,查找性 能将受到严重阻碍。

哈希算法亦是影响查找效率的重要因素之一。如果所使用的哈希算法只能产生少量的不同哈希值(从而也只能产生少量的不同的哈希表索引值),查找性能也同 样将被降低。

优化Map查找性能的最有效途径是尽可能的增大哈希表以降低因索引值相同而产生冲突的可能。微软推荐将哈希表的大小设置为Map中所存储元素数目的 110% ~120%,以使得Map的应用性能在内存消耗和查找效率之间取得相对平衡。

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

map.InitHashTable(1200);

式中,假设Map中要存储1000个元素,按照微软公司的推荐,将哈希表的大小扩展到实际存储元素数目的120%,即设置Map大小为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类

  有关MFC中的CMap类的概况,上面的文字段落中已经陆续提及,在此不再赘言。下面,列出CMap类的基本成员函数,并通过一个 简短的程序片段来粗略地演示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

在上面的应用程序片段中,我们可以了解有关CMap类的在通常情况下的使用方法。

1、首先我们使用CMap模板类来定义一个实例--myMap对象。

2、紧接着要做的是对myMap对象的哈希表的大小进行初始化设置。此时,应该先对myMap可能的容量需求进行估计,然后选择适当大小的奇数-- 或者,有可能的话,使用素数的效果会更好一些--来作为哈希表的初始值。

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

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

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

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

cmap用法,很详细(转)相关推荐

  1. 3款大屏播放软件,用法很详细,用于大屏幕播放视频和图片

    要在展厅或户外的LED大屏幕上长时间循环播放视频或图片,就需要用到大屏播放软件,笔者整理几款好用的给大家. 一.动屏大屏播放软件 特点 这款软件的特点是配置简单,界面美观,即插即用,将视频或图片文件拷 ...

  2. Java Scanner类的常用方法及用法(很详细)

    Java Scanner类的方法及用法(很详细) Scanner类简介 Java 5添加了java.util.Scanner类,这是一个用于扫描输入文本的新的实用程序.它是以前的StringToken ...

  3. 介绍一篇关于session的好文章,写的很详细

    介绍一篇关于session的好文章,写的很详细 目录: 一.术语session 二.HTTP协议与状态保持 三.理解cookie机制 四.理解session机制 五.理解javax.servlet.h ...

  4. python教程很详细_Python编程入门教程:从入门到高级,非常详细

    本文的资料和内容是我下载的,觉得非常有用,于是转过来大家瞧瞧: 这里给初学Python的朋友提供一些建议和指导吧.大神请无视, 俗话说:授人以鱼不如授人以渔.所以我这里只是阐述学习过程,并不会直接详细 ...

  5. 转:Java多线程学习(总结很详细!!!)

    Java多线程学习(总结很详细!!!) 此文只能说是java多线程的一个入门,其实Java里头线程完全可以写一本书了,但是如果最基本的你都学掌握好,又怎么能更上一个台阶呢? 本文主要讲java中多线程 ...

  6. [转载]Matlab绘图-很详细,很全面(包含各种标示符的输入方法)

    原文地址:Matlab绘图-很详细,很全面(包含各种标示符的输入方法)作者:deman Matlab绘图-很详细,很全面 Matlab绘图 强大的绘图功能是Matlab的特点之一,Matlab提供了一 ...

  7. python or的用法_详细介绍Python中and和or实际用法

    and 和 or 的特殊性质 在Python 中,and 和 or 执行布尔逻辑演算,但是它们并不返回布尔值:而是,返回它们实际进行比较的值之一.下面来看一下实例.>>> 'a' a ...

  8. Iframe 用法的详细讲解

    Iframe 用法的详细讲解 把iframe解释成"浏览器中的浏览器"很是恰当 <iframe frameborder=0 width=170 height=100 marg ...

  9. yum配置与使用(很详细)

    原文地址为: yum配置与使用(很详细) yum的配置一般有两种方式,一种是直接配置/etc目录下的yum.conf文件,另外一种是在/etc/yum.repos.d目录下增加.repo文件. 一.y ...

  10. Makefile教程,很详细的东西,收藏了~长文慎点=。=

    陈皓 概述  -- 什么是makefile?或许很多Winodws的程序员都不知道这个东西,因为那些Windows的IDE都为你做了这个工作,但我觉得要作一个好的和professional的程序员,m ...

最新文章

  1. python从入门到实践回顾——字典
  2. 捕捉到了异常继续循环_前端异常处理最佳实践
  3. EAS WebService部署
  4. C++ STL pair方法详解
  5. java flushdb_JAVA - Redis
  6. 在RedHat Enterprise Linux 上Oracle 9i的安装配置与调优
  7. 蓝桥杯 ADV-88 算法提高 输出正反三角形
  8. Tomcat JMX
  9. IDEA的插件和一些主题推荐
  10. 【Unity】雷达+Unity +TUIO 介绍一
  11. 计算机网络基础(TCP/IP)
  12. c++ string常用函数
  13. vue3+vite+ts 通过svg-sprite-loader 插件使用自定义图标
  14. 锚点链接点击锚点后不改变url的方法
  15. 【BUUCTF】Crypto题解
  16. 如何成为一名优秀的程序员(六)
  17. 测试软件cpu占用率 可以用,CPU占用率检测工具
  18. 将自动获取IP改为固定IP
  19. WiFi万能钥匙万玉权:管理应该是“自下而上”
  20. 苯乙烯基/綦乙烯基/苯丁烯基修饰BODIPY染料|聚合物BODIPY染料定制服务

热门文章

  1. SAP小技巧之 实现SMARTFORM打印中的小计与总计
  2. PS 2019 Mac版 自学入门系列(八)—— 替换背景
  3. 3.8女王节里的宝妈生意经
  4. 大学英语综合教程四 Unit 4 课文内容英译中 中英翻译
  5. 移动机器人(四)四轴飞行器
  6. 【大厂智力题】64匹马,8个赛道,找出前4名最少比赛多少场?
  7. 判断两个单词是否互为变位词
  8. 光是无限远服务器的花海,第21次云垂纵横计划服务器互通投票结果公告
  9. OCI动态链接ORACLE
  10. React + Springboot + Quartz,从0实现Excel报表自动化