﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>C++博客-吴秦（Saylor）-随笔分类-C/C++ Internals</title><link>http://www.cppblog.com/09212744/category/14073.html</link><description /><language>zh-cn</language><lastBuildDate>Sat, 19 Jun 2010 17:06:34 GMT</lastBuildDate><pubDate>Sat, 19 Jun 2010 17:06:34 GMT</pubDate><ttl>60</ttl><item><title>C++ Internals: STL之Map</title><link>http://www.cppblog.com/09212744/archive/2010/06/19/118235.html</link><dc:creator>吴秦（Saylor）</dc:creator><author>吴秦（Saylor）</author><pubDate>Sat, 19 Jun 2010 04:25:00 GMT</pubDate><guid>http://www.cppblog.com/09212744/archive/2010/06/19/118235.html</guid><wfw:comment>http://www.cppblog.com/09212744/comments/118235.html</wfw:comment><comments>http://www.cppblog.com/09212744/archive/2010/06/19/118235.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.cppblog.com/09212744/comments/commentRss/118235.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/09212744/services/trackbacks/118235.html</trackback:ping><description><![CDATA[<h4><strong>概述</strong></h4>
<p>Map是标准<strong>关联式容器</strong>（<em>associative</em> <em>container</em>）之一，一个map是一个键值对序列，即（key ,value）对。它提供基于key的<strong>快速</strong>检索能力，在一个map中key值是唯一的。map提供双向迭代器，即有从前往后的（iterator），也有从后往前的（reverse_iterator）。</p>
<p><strong>map要求能对key进行&lt;操作，且保持按key值递增有序</strong>，因此map上的迭代器也是递增有序的。如果对于元素并不需要保持有序，可以使用<strong>hash_map</strong>。</p>
<p>map中key值是唯一的，如果马匹中已存在一个键值对(昵称,密码):("skynet",407574364)，而我们还想插入一个键值对("skynet",472687789)<span style="text-decoration: line-through;">则会报错</span>（<span style="color: #ff0000;">不是报错，准确的说是，返回插入不成功！</span>）。而我们又的确想这样做，即一个键对应多个值，幸运的是multimap可是实现这个功能。</p>
<p>下面我们用实例来深入介绍<em>map</em>、<em>multimap</em>，主要内容如下：</p>
<ul>
    <li>1、例子引入  </li>
    <li>2、map中的类型定义  </li>
    <li>3、map中的迭代器和键值对  </li>
    <li>4、map中的构造函数与析构函数  </li>
    <li>5、map中的操作方法  </li>
    <li>6、再议map的插入操作  </li>
    <li>7、[]不仅插入  </li>
    <li>8、multimap  </li>
    <li>9、总结 </li>
</ul>
<h4><strong>1、例子引入</strong></h4>
<p>有一个服务器manager维护着接入服务器的client信息，包括clinetId、scanRate、socketAddr等等。我们定义一个结构体保存scanRate、socketAddr信息。如下：</p>
<pre class="brush: cpp;">typedef    int    clientId;
typedef struct{
int scanRate;
string socketAddr;
}clientInfo;</pre>
<p>我们用map保存这些信息：clientId为键key，clientInfo为值。这样我们可以通过clientId快速检索到client的相关信息，我们可以这样定义：</p>
<pre class="brush: cpp;">map&lt;clientId,clientInfo&gt; clientMap;</pre>
<p>这样我们定义了一个clientMap，如果我们要定义多个这样的map，需要多次写map&lt;clientId,clientInfo&gt; 变量名。为了避免这样情况，我们通常为map&lt;clientId,clientInfo&gt;定义个别名，如：</p>
<pre class="brush: cpp;">typedef map&lt;clientId,clientInfo&gt; clientEdp;
clientEdp clientMap;</pre>
<p>之后我们就可以像定义clientMap一样定义map&lt;clientId,clientInfo&gt;对象，这样的好处还有：如果我们需要修改map的定义，只需要在一处修改即可，避免修改不彻底造成的不一致现象。</p>
<p>我们这就完成了需要的map的定义，如果不定义或没有在它上面的操作的话，就像定义类而没有方法一样，意义不大或毫无意义。幸运的是，STL提供了这些常用操作：排序（注：map是不能也不要排序的，因为map本身已经排好序了）、打印、提取子部分、移除元素、添加元素、查找对象，就像数据库的增删改查操作！现在我们详细介绍这些操作，并逐步引入<em>hash_map</em>、<em>multimap</em>。</p>
<h4><strong>2、map中的类型定义</strong></h4>
<p>关联数组（<em>associative array</em>）是最有用的用户定义类型之一，经常内置在语言中用于文本处理等。一个关联数组通常也称为map，有时也称字典（<em>dictionary</em>），保存一对值。第一个值称为key、第二个称为映射值mapped-value。</p>
<p>标准map是定义在std命名空间中的一个模板，并表示为&lt;map&gt;。它首先定义了一组标准类型名字：</p>
<pre class="brush: cpp;">template&lt;class Key,class T,class Cmp=less&lt;key&gt;,
class A=allocator&lt;pair&lt;const Key,T&gt;&gt;
class std::map
{
public:
//types
typedef Key    key_type;
typedef T    mapped_type;
typedef pair&lt;const Key,T&gt;    value_type;
typedef    Cmp    key_compare;
typedef A    allocator_type;
typedef    typename    A::reference    reference;
typedef    typename    A::const_reference    const_reference;
typedef    implementation_define1    iterator;
typedef implementation_define2    const_iterator;
typedef    typename    A::size_type    size_type;
typedef    typename    A::difference_type    difference_type;
typedef    std::reverse_iterator&lt;iterator&gt;    reverse_iterator;
typedef    std::reverse_iterator&lt;const_iterator&gt;    const_reverse_iterator;
//...
}</pre>
<p>注意：map的value_type是一个(key,value)对，映射值的被认为是mapped_type。因此，一个map是一个pair&lt;const Key,mapped_type&gt;元素的序列。从const Key可以看出，map中键key是不可修改的。</p>
<p>不得不提的是map定义中Cmp和A都是可选项。Cmp是定义在元素之间的比较方法，默认是&lt;操作；A即allocator用来<strong>分配</strong>和<strong>释放</strong>map总键值对所需使用的内存，没有指定的话即默认使用的是STL提供的，也可以自定义allocator来管理内存的使用。多数情况，我们不指定这两个选项而使用默认值，这样我们定义map就像下面这样：</p>
<pre class="brush: cpp;">map&lt;int,clientInfo&gt; clientMap;</pre>
<p>Cmp和A都缺省。 通常，实际的迭代器是实现定义的，因为map很像使用了树的形式，这些迭代器通常提供树遍历的某种形式。逆向迭代器是使用标准的reverse_iterator模板构造的。</p>
<h4><strong>3、map中的迭代器和键值对</strong></h4>
<p>map提供惯常的返回迭代器的一组函数，如下所示：</p>
<pre class="brush: cpp;">template&lt;class Key,class T,class Cmp=less&lt;key&gt;,
class A=allocator&lt;pair&lt;const Key,T&gt;&gt;
class std::map
{
public:
//...
//iterators
iterator    begin();
const_iterator    begin()    const;
iterator    end();
const_iterator    end()    const;
reverse_iterator    rbegin();
const_reverse_iterator    rbegin()    const;
reverse_iterator    rend();
const_reverse_iterator    rend()    const;
//...
}</pre>
<p>map上的迭代器是pair&lt;const Key,mapped_type&gt;元素序列上简单的迭代。例如，我们可能需要打印出所有的客户端信息，像下面的程序这样。为了实现这个，我们首先向《例子引入》中定义的clientEdp中插入数据，然后打印出来：</p>
<pre class="brush: cpp;">#include&lt;iostream&gt;
#include&lt;map&gt;
#include&lt;string&gt;
using namespace std;
typedef    int    clientId;
typedef struct{
int scanRate;
string socketAddr;
}clientInfo;
int main(int argc,char** argv)
{
typedef map&lt;clientId,clientInfo&gt; clientEdp;
typedef map&lt;clientId,clientInfo&gt;::const_iterator iterator;
clientEdp clients;
clientInfo client[100];
char str[10];
string strAddr("socket addr client ");
for(int i=0;i&lt;100;i++)
{
client[i].scanRate=i+1;
//convert int to char*
itoa(i+1,str,10);
//concatenate strAddr and str
client[i].socketAddr=strAddr+str;
cout&lt;&lt;client[i].socketAddr&lt;&lt;endl;
clients.insert(
make_pair(i+1,client[i]));
}
delete str;
for(iterator i=clients.begin();i!=clients.end();i++)
{
cout&lt;&lt;"clientId:"&lt;&lt;i-&gt;first&lt;&lt;endl;
cout&lt;&lt;"scanRate:"&lt;&lt;i-&gt;second.scanRate&lt;&lt;endl;
cout&lt;&lt;"socketAddr:"&lt;&lt;i-&gt;second.socketAddr&lt;&lt;endl;
cout&lt;&lt;endl;
}
}</pre>
<p>一个map迭代器以key升序方式表示元素，因此客户端信息以cliendId升序的方式输出。运行结果可以证明这一点，运行结果如下所示：</p>
<p><a href="http://www.cppblog.com/images/cppblog_com/09212744/WindowsLiveWriter/CInternalsSTLMap_132F1/image_2.png"><img style="border-width: 0px; display: block; float: none; margin-left: auto; margin-right: auto;" title="image" alt="image" src="http://www.cppblog.com/images/cppblog_com/09212744/WindowsLiveWriter/CInternalsSTLMap_132F1/image_thumb.png" width="313" border="0" height="433"></a> </p>
<p align="center">图1、程序运行结果</p>
<p>我们以first引用键值对的key，以second引用mapped value，且不用管key和mapped value是什么类型。其实pair在std的模板中是这样定义的：</p>
<pre class="brush: cpp;">template &lt;class    T1,class T2&gt;struct std::pair{
typedef    T1    first_type;
typedef    T2    second_type;
T1    first;
T2    second;
pair():first(T1()),second(T2()){}
pair(const T1&amp; x,const T2&amp; y):first(x),second(y){}
template&lt;class U,class V&gt;
pair(const pair&lt;U,V&gt;&amp; p):first(p.first),second(p.second){}
}</pre>
<p>即map中，key是键值对的第一个元素且mapped value是第二个元素。pair的定义可以在&lt;utility&gt;中找到，pair提供了一个方法方便创建键值对：</p>
<pre class="brush: cpp;">template &lt;class T1,class T2&gt;pair&lt;T1,T2&gt;
std::make_pair(const T1&amp; t1,const T2&amp; t2)
{
return pair&lt;T1,T2&gt;(t1,t2);
}</pre>
<p>上面的例子中我们就用到了这个方法来创建(clientId,clientInfo)对，并作为Insert()方法的参数。每个pair默认初始化每个元素的值为对应类型的默认值。</p>
<h4><strong>4、map中的构造函数与析构函数</strong></h4>
<p>map类惯常提供了构造函数和析构函数，如下所示：</p>
<pre class="brush: cpp;">template&lt;class Key,class T,class Cmp=less&lt;key&gt;,
class A=allocator&lt;pair&lt;const Key,T&gt;&gt;
class std::map
{
//...
//construct/copy/destroy
explicit map(const Cmp&amp;=Cmp(),const A&amp;=A());
template&lt;class In&gt;map(In first,In last,
const Com&amp;=Cmp(),const A&amp;=A());
map(const map&amp;);
~map();
map&amp; operator=(const map&amp;);
//...
}</pre>
<p>复制一个容器意味着为它的每个元素分配空间，并拷贝每个元素值。这样做是性能开销是很大的，应该仅当需要的时候才这样做。<strong>因此，map传的是引用</strong>。</p>
<h4><strong>5、map中的操作方法</strong></h4>
<p>前面我们已经说过，如果map中仅定义了一些key、mapped value类型的信息而没有操作方法，就如定义个仅有字段的类意义不大甚至毫无意义。由此可见map中定义操作方法非常重要！前面的例子我们就用到了不少方法，如返回迭代器的方法begin()、end()，键值对插入方法insert()。下面我们对map中的操作方法做个全面的介绍：</p>
<pre class="brush: cpp;">template&lt;class Key,class T,class Cmp=less&lt;key&gt;,
class A=allocator&lt;pair&lt;const Key,T&gt;&gt;
class std::map
{
//...
//map operations
//find element with key k
iterator find(const key_type&amp; k);
const_iterator find(const key_type&amp; k) const;
//find number of elements with key k
size_type count() const;
//find first element with key k
iterator lower_bound(const key_type&amp; k);
const_iterator lower_bound(const key_type&amp; k) const;
//find first element with key greater than k
iterator upper_bound(const key_type&amp; k);
const_iterator upper_bound(const key_type&amp; k) const;
//insert pair(key,value)
pair&lt;iterator,bool&gt;insert(const value_type&amp; val);
iterator insert(iterator pos,const value_type&amp; val);
template&lt;class In&gt;void insert(In first,In last);
//erase element
void erase(iterator pos);
size_type erase(const key_type&amp; k);
void erase(iterator first,iterator last);
void clear();
//number os elements
size_type size() const;
//size of largest possible map
size_type max_size() const;
bool empty() const{return size()==0;}
void swap(map&amp;);
//...
}</pre>
<p>上面这些方法基本都能顾名思义（PS.由此可见，命名有多重要，我们平时要养成好的命名习惯，当然注释也必不可少！）。虽然已经非常清楚了了，但我还是想讲解一下以消除不惜要的误解和更好地应用这些方法。</p>
<ul>
    <li>find(k)方法简单地返回键值为k的元素的迭代器；如果没有元素的键值为k，则返回map的end()迭代器。由于map是按键key升序排列，所有查找的复杂度只有O(logN)。因此，我们通常会这样用这个方法： <br>
    <pre class="brush: cpp;">#include&lt;iostream&gt;
    #include&lt;map&gt;
    #include&lt;string&gt;
    using namespace std;
    typedef    int    clientId;
    typedef struct{
    int scanRate;
    string socketAddr;
    }clientInfo;
    int main(int argc,char** argv)
    {
    typedef map&lt;clientId,clientInfo&gt; clientEdp;
    typedef map&lt;clientId,clientInfo&gt;::const_iterator iterator;
    clientEdp clients;
    clientInfo client[100];
    char* str=new char[10];
    string strAddr("socket addr client ");
    for(int i=0;i&lt;100;i++)
    {
    client[i].scanRate=i+1;
    //convert int to char*
    itoa(i+1,str,10);
    //concatenate strAddr and str
    client[i].socketAddr=strAddr+str;
    clients.insert(
    make_pair(i+1,client[i]));
    }
    delete str;
    <span style="color: #ff0000;">    </span><strong><span style="color: #ff0000;">clientId id=10;
    iterator i=clients.find(id);
    if(i!=clients.end()){
    cout&lt;&lt;"clientId: "&lt;&lt;id
    &lt;&lt;" exists in clients"&lt;&lt;endl;
    }
    else{
    cout&lt;&lt;"clientId: "&lt;&lt;id
    &lt;&lt;" doesn't exist in clients"&lt;&lt;endl;
    }</span></strong>
    }</pre>
    </li>
    <li>insert()方法 试图将一个（Key,T）键值对加入map。因为键时唯一的，所以仅当map中不存在键值为k的键值对时插入才成功。该方法的返回值为pair&lt;iterator,bool&gt;，如果插入成功bool值为TRUE，iterator指向插入map中后的键值对。如下代码： <br>
    <pre class="brush: cpp;">#include&lt;iostream&gt;
    #include&lt;map&gt;
    #include&lt;string&gt;
    using namespace std;
    typedef    int    clientId;
    typedef struct{
    int scanRate;
    string socketAddr;
    }clientInfo;
    int main(int argc,char** argv)
    {
    typedef map&lt;clientId,clientInfo&gt; clientEdp;
    typedef map&lt;clientId,clientInfo&gt;::const_iterator iterator;
    clientEdp clients;
    clientId id=110;
    clientInfo cltInfo;
    cltInfo.scanRate=10;
    cltInfo.socketAddr="110";
    pair&lt;clientId,clientInfo&gt; p110(id,cltInfo);
    pair&lt;iterator,bool&gt; p=clients.insert(p110);
    if(p.second){
    cout&lt;&lt;"insert success!"&lt;&lt;endl;
    }
    else{
    cout&lt;&lt;"insert failed!"&lt;&lt;endl;
    }
    //i points to clients[110];
    iterator i=p.first;
    cout&lt;&lt;i-&gt;first&lt;&lt;endl;
    cout&lt;&lt;i-&gt;second.scanRate&lt;&lt;endl;
    cout&lt;&lt;i-&gt;second.socketAddr&lt;&lt;endl;
    }</pre>
    </li>
</ul>
<p>上面我们看出，这里我们插入键值对是首先声明一个键值对pair&lt;clientId,clientInfo&gt; p110(id,cltInfo); 然后再插入，这个我们之前make_pair方法不一样，make_pair方法用的比较多。</p>
<ul>
    <li>erase()方法用法比较简单，比如像清除clientId为110的键值对，我们只需要对clients调用erase方法：<span style="color: #ff8040;">clients.erase(clients.find(110));</span>或者我们想清除clientId从1到10的键值对，我们可以这样调用erase()方法：<span style="color: #ff8040;">clients.erase(clients.finds(1),clients.find(10));</span>简单吧！别得意，你还需要注意，如果find(k)返回的是end()，这样调用erase()方法则是一个严重的错误，会对map造成破坏操作。 </li>
</ul>
<h4><strong>6、再议map的插入操作</strong></h4>
<p>前面我们介绍了利用map的插入方法insert()，声明键值对pair或make_pair生成键值对然后我们可以轻松的将键值对插入map中。其实map还提供了更方便的插入操作利用下标（subscripting，[]）操作，如下：</p>
<pre class="brush: cpp;">clientInfo cltInfo;
cltInfo.scanRate=10;
cltInfo.socketAddr="110";
<strong>clients[110]=cltInfo;</strong></pre>
<p>这样我们就可以简单地将键值对插入到map中了。下标操作在map中式这样定义的：</p>
<pre class="brush: cpp;">template&lt;class Key,class T,class Cmp=less&lt;key&gt;,
class A=allocator&lt;pair&lt;const Key,T&gt;&gt;
class std::map
{
//...
//access element with key k
mapped_type&amp; operator[](const key_type&amp; k);
//...
}</pre>
<p>我们来分析一下应用[]操作，插入键值对的过程：<strong>检查键k是否已经在map里。如果不，就添加上，以v作为它的对应值。如果k已经在map里，它的关联值被更新成v。这里首先，查找110不在map中则创建一个键为110的键值对，并将映射值设为默认值，这里scanRate为0，socketAddr为空；然后将映射值赋为cltInfo。 </strong>如果110在map中已经存在的话，则只是更新以110为键的映射值。</p>
<p>从上面的分析可知：如果大量这样插入数据，会严重影响效率！如果你考虑效率问题，请使用insert操作。insert方法，节省了三次函数调用：一个建立临时的默认映射值的对象，一个销毁那个临时的对象和一个对映射值的赋值操作。</p>
<p><strong>Note1：</strong>如果k已经存在map中，[]效率反而比insert的效率高，而且更美观！如果能够兼顾这两者那岂不是很美妙！其实我们重写map中的[]操作：首先判断k是否已经在map中，如果没有则调用insert操作，否则调用内置的[]操作。如下列代码：</p>
<pre class="brush: cpp;">//////////////////////////////////////////////
///@param MapType-map的类型参数
///@param KeyArgType-键的类型参数
///@param ValueArgtype-映射值的类型参数
///@return 迭代器，指向键为k的键值对
//////////////////////////////////////////////
template&lt;typename MapType,
typename KeyArgType,
typename ValueArgtype&gt;
typename MapType::iterator
efficientAddOrUpdate(MapType&amp; m,
const KeyArgType&amp; k,
const ValueArgtype&amp; v)
{
typename MapType::iterator Ib =    m.lower_bound(k);
if(Ib != m.end()&amp;&amp;!(m.key_comp()(k,Ib-&gt;first))) {
//key已经存在于map中做更新操作
Ib-&gt;second = v;
return Ib;
}
else{
//key不存在map中做插入操作
typedef typename MapType::value_type MVT;
return m.insert(Ib, MVT(k, v));
}
}</pre>
<p><strong>Note2：</strong>我们视乎还忽略了一点，如果映射值mapped value的类型没有默认值，怎么办？这种情况请勿使用[]操作插入。</p>
<h4><strong>7、[]不仅插入</strong></h4>
<p>通过[]操作不仅仅是插入键值对，我们也可以通过键key检索出映射值mapped value。而且我们利用[]操作可以轻松地统计信息，如有这样这样一些键值对（book-name，count）对：</p>
<p>(book1,1)、(book2,2)、(book1,2)、(book3,1)、(book3,5)</p>
<p>我们计算每种book的数量总和。我们可以这样做：将它们读入一个map&lt;string,int&gt;：</p>
<pre class="brush: cpp;">#include&lt;iostream&gt;
#include&lt;map&gt;
#include&lt;string&gt;
using namespace std;
int main(int argc,char** argv)
{
map&lt;string,int&gt; bookMap;
string book;
int count;
int total=0;
while(cin&gt;&gt;book&gt;&gt;count)
bookMap[book]+=count;
map&lt;string,int&gt;::iterator i;
for(i=bookMap.begin();i!=bookMap.end();i++)
{
total+=i-&gt;second;
cout&lt;&lt;i-&gt;first&lt;&lt;'\t'&lt;&lt;i-&gt;second&lt;&lt;endl;
}
cout&lt;&lt;"total count:"&lt;&lt;total&lt;&lt;endl;
}</pre>
<p>结果如下所示：（注意按住ctrl+z键结束输入）</p>
<p><a href="http://www.cppblog.com/images/cppblog_com/09212744/WindowsLiveWriter/CInternalsSTLMap_132F1/image_4.png"><img style="border-width: 0px; display: block; float: none; margin-left: auto; margin-right: auto;" title="image" alt="image" src="http://www.cppblog.com/images/cppblog_com/09212744/WindowsLiveWriter/CInternalsSTLMap_132F1/image_thumb_1.png" width="254" border="0" height="218"></a> </p>
<p align="center">图2、程序运行结果</p>
<h4><strong>8、multimap</strong></h4>
<p>前面介绍了map，可以说已经非常清晰了。如果允许clientId重复的话，map就无能为力了，这时候就得multimap上场了！<strong>multimap允许键key重复，即一个键对应多个映射值。</strong>其实除此之外，multimap跟map是很像的，我们接下来在map的基础上介绍multimap。</p>
<p>multimap在std中的定义跟map一样只是类名为multimap，multimap几乎有map的所有方法和类型定义。</p>
<ul>
    <li>multimap不支持[]操作；但map支持
    </li>
    <li>multimap的insert方法返回的是一个迭代器iterator，没有bool值；而map值（iterator，bool）的元素对
    </li>
    <li>对应equal_range()、方法：
    <div>
    <pre>pair&lt;iterator,iterator&gt; equal_range(<span style="color: #0000ff;">const</span> key_type&amp; k);
    pair&lt;const_iterator,const_iterator&gt;
    equal_range(<span style="color: #0000ff;">const</span> key_type&amp; k) <span style="color: #0000ff;">const</span>;
    <span style="color: #008000;">//find first element with key k</span>
    iterator lower_bound(<span style="color: #0000ff;">const</span> key_type&amp; k);
    const_iterator lower_bound(<span style="color: #0000ff;">const</span> key_type&amp; k) <span style="color: #0000ff;">const</span>;
    <span style="color: #008000;">//find first element with key greater than k</span>
    iterator upper_bound(<span style="color: #0000ff;">const</span> key_type&amp; k);
    const_iterator upper_bound(<span style="color: #0000ff;">const</span> key_type&amp; k) <span style="color: #0000ff;">const</span>;</pre>
    </div>
    虽然在map和multimap都有，显然对multimap有更多的意义！equal_range()方法返回一个键key对应的多个映射值的上界和下界的键值对的迭代器、lower_bound()方法返回键multimap中第一个箭为key的键值对迭代器、upper_bound()方法返回比key大的第一个键值对迭代器。 </li>
</ul>
<p>假设我们想取出键为key的所有映射值，我们可以这样做：</p>
<pre class="brush: cpp;">#include&lt;iostream&gt;
#include&lt;map&gt;
#include&lt;string&gt;
using namespace std;
typedef int clientId;
typedef struct{
int scanRate;
string socketAddr;
}clientInfo;
int main(int argc,char** argv)
{
typedef multimap&lt;clientId,clientInfo&gt; clientEdp;
typedef multimap&lt;clientId,clientInfo&gt;::const_iterator iterator;
clientEdp clients;
clientInfo client[20];
char* str=new char[10];
string strAddr("socket addr client ");
for(int i=0;i&lt;10;i++)
{
client[i].scanRate=i+1;
//convert int to char*
itoa(i+1,str,10);
//concatenate strAddr and str
client[i].socketAddr=strAddr+str;
clients.insert(
make_pair(10,client[i]));
}
for(int i=10;i&lt;20;i++)
{
client[i].scanRate=i+1;
//convert int to char*
itoa(i+1,str,10);
//concatenate strAddr and str
client[i].socketAddr=strAddr+str;
clients.insert(
make_pair(i+1,client[i]));
}
delete str,strAddr;
<strong>    //find elements with key 10
iterator lb=clients.lower_bound(10);
iterator ub=clients.upper_bound(10);</strong>
for(iterator i=lb;i!=ub;i++)
{
cout&lt;&lt;"clientId:"&lt;&lt;i-&gt;first&lt;&lt;endl;
cout&lt;&lt;"scanRate:"&lt;&lt;i-&gt;second.scanRate&lt;&lt;endl;
cout&lt;&lt;"socketAddr:"&lt;&lt;i-&gt;second.socketAddr&lt;&lt;endl;
cout&lt;&lt;endl;
}
}</pre>
<p>（说明：实际上，一般是不允许clientId重复的，这里只是为了举例。）这样是不是感觉很丑呢！事实上，我们可以更简单的这样：</p>
<pre class="brush: cpp;">//find elements with key 10
<strong>pair&lt;iterator,iterator&gt; p=clients.equal_range(10);</strong>
for(iterator i=p.first;i!=p.second;i++)
{
cout&lt;&lt;"clientId:"&lt;&lt;i-&gt;first&lt;&lt;endl;
cout&lt;&lt;"scanRate:"&lt;&lt;i-&gt;second.scanRate&lt;&lt;endl;
cout&lt;&lt;"socketAddr:"&lt;&lt;i-&gt;second.socketAddr&lt;&lt;endl;
cout&lt;&lt;endl;
}</pre>
<h4><strong>总结</strong></h4>
<p>map是一类关联式容器。它的特点是增加和删除节点对迭代器的影响很小，除了那个操作节点，对其他的节点都没有什么影响。对于迭代器来说，可以修改实值，而不能修改key。 </p>
<p>map的功能：</p>
<ul>
    <li>自动建立Key －value的对应。key 和value可以是任意你需要的类型。
    </li>
    <li>根据key值快速查找记录，查找的复杂度基本是Log(N)。
    </li>
    <li>快速插入Key - Value 记录。
    </li>
    <li>快速删除记录
    </li>
    <li>根据Key 修改value记录。
    </li>
    <li>遍历所有记录。 </li>
</ul>
<p>展望：本文不知不觉写了不少字了，但仍未深入涉及到map定义的第3个和第4个参数，使用的都是默认值。</p>
<p>template&lt;class Key,class T,class Cmp=less&lt;key&gt;, <br>&nbsp;&nbsp;&nbsp; class A=allocator&lt;pair&lt;const Key,T&gt;&gt;</p>
<p>感兴趣者，请查找相关资料or下面留言希望看到单独开篇介绍map第3个和第4个参数。您的支持，我的动力！PS：在此文的原因，在与公司做项目用到了map特此总结出来与大家共享，不过在进行个人总结过程中，难免会有疏漏或不当之处，请不吝指出。</p>
<h4><strong>参考文献：</strong></h4>
<p>【1】《The C++ Programming Language (Special Edition)》</p>
<p>【2】《Effective STL》</p><img src ="http://www.cppblog.com/09212744/aggbug/118235.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/09212744/" target="_blank">吴秦（Saylor）</a> 2010-06-19 12:25 <a href="http://www.cppblog.com/09212744/archive/2010/06/19/118235.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>