您的位置:首页 > 编程语言 > C语言/C++

C++容器(四):map类型

2015-10-01 21:46 381 查看

map 类型

map
是键-值对的集合。
map
类型通常可以理解为关联数组:可以使用键作为下标来获取一个值,正如内置数组类型一样。而关联的本质在于元素的值与某个特定的键相关联,而非通过元素在数组内的位置来获取。

要使用
map
对象,必须包含
map
头文件:

[code]#include <map>


在定义
map
对象时,必须指明键和值的类型:

[code]map<string, int> wordCount; // empty map from string to int


1
map
对象的构造函数

操作含义
map<k, v> m
创建一个名为
m
map
对象,其键和值的类型分别为
k
v
map<k, v> m(m2)
创建
m2
的副本
m
m
m2
必须具有相同的键类型和值类型
map<k, v> m(b, e)
创建
map
类型的对象
m
,存储迭代器
b
e
标记范围内的所有元素副本。元素的类型必须能够转换为
pair<const k, v>
对于键类型,唯一的约束就是必须支持
<
操作,至于是否支持其他关系或相等运算,则不做要求。


2
map
定义的类型

包括:键类型,值类型以及键-值(
pair
)类型。

类型含义
map<k, v>::key_type
map
容器中,用作索引的键的类型
map<k, v>::mapped_type
map
类型中,键所关联的值的类型
map<k, v>::value_type
一个
pair
类型,它的
first
元素具有
const map<k, v>::key_type
类型,而
second
则为
map<k, v>::mapped_type
类型
map
迭代器进行解引用将产生
pair
类型的对象

对迭代器进行解引用时,将获得一个引用,指向容器的一个
value_type
类型值。

[code]map<string, int>::iterator map_it = wordCount.begin();
// *map_it is a reference to a pair<const string, int> object
cout << map_it->first << " " << map_it->second << endl; // print the key and value of the element

map_it->first = "new key"; // error: key is const type
++ map_it->second; // ok: we can change value through the iterator


3 给
map
添加元素

map
中添加键-值元素对时,有两种方式:一种是先用下标操作符获取元素,然后给获取的元素赋值,一种是使用
insert
成员函数实现。

[b]3.1 使用下标访问
map
对象
[/b]

如编写下段程序时:

[code]map<string, int> wordCount; // empty map
// insert default initialized element with key "Anna"; then assign 1 to its value
wordCount["Anna"] = 1;


将发生以下事情:


在wordCount中查找键为
Anna
的元素,没有找到;

将一个新的键-值对插入到
wordCount
中。它的键是
const string
类型的对象,保存
Anna
。而它的值则采用值初始化,这就意味着在本例中值为0;

将这个新的键-值对插入到
wordCount
中;

读取插入的新元素,并将它的值赋为1。



下标操作符返回值的使用

通常来说,下标操作符返回左值。它返回的左值是特定键所关联的值。

[code]cout << wordCount["Anna"] <<endl;  // fetch element indexed by Anna; ptint 1
++ wordCount["Anna"];              // fetch the element and add one to it
cout << wordCount["Anna"] << endl; // fetch element and print it; print 2


有别于
vector
string
类型,
map
下标操作符返回的类型与对
map
迭代器进行解引用获得的类型不相同。


下标行为的编程意义

对于
map
容器,如果下标表示的键在容器中不存在,则添加新元素,这一特性可使程序惊人的简练:

[code]// count the number of times each word occurs in the input
map<string, int> wordCount; // empty map from string to int
string word;
while ( cin >> word )
    ++ wordCount[word];


这段程序创建一个
map
对象,用来记录每个单词出现的次数,
while
循环每次从标准输入读取一个单词,如果这是一个新的单词,则在
wordCount
中添加以该单词为索引的新元素,如果读入单词已经在
map
对象中,则将它所对应的值加1。

[b]3.2
insert
成员函数
[/b]

map
容器的
insert
成员与顺序容器的类似,但有一点要注意:必须考虑键的作用。

操作含义
m.insert(e)
e
是一个用在
m
上的
value_type
类型的值。如果键(
e.first
)不在
m
中,则插入一个值为
e.second
的新元素;如果键在
m
中已经存在,则保持
m
不变。该函数返回一个
pair
类型对象,包含指向键为
e.first
的元素的
map
迭代器,以及一个
bool
的对象,表示是否插入了该元素
m.insert(begin, end)
begin
end
是标记元素范围的迭代器,其中的元素必须为
m.value_type
类型的键-值对。对于该范围内的所有元素,如果其键在
m
中不存在,则将该键及其关联的值插入
m
。返回
void
类型
m.insert(iter, e)
e
是一个用在
m
上的
value_type
类型的值。如果键(
e.first
)不在
m
中,则创建新元素,并以迭代器
iter
为起点搜索新元素存储的位置。返回一个迭代器,指向
m
中具有给定键的元素
insert
代替下标运算

使用下标给
map
容器添加新元素时,元素的值部分将采用值初始化。通常,我们会立即为其赋值,其实就是对同一个对象进行初始化并赋值。而
insert
其语法更加紧凑:

[code]// if Anna not already in wordCount, inserts new element with value 1
wordCount.insert( map<string, int>::value_type("Anna", 1) );


这个
insert
函数版本的实参:

[code]map<string, int>::value_type("Anna", 1)


是一个新创建的
pair
对象,将直接插入到
map
容器中。但是传递给
insert
的实参相当笨拙,有两种方法可以简化:

[code]// first
wordCount.insert( make_pair("Anna", 1) );

// second
typedef map<string, int>::value_type valueType;
wordCount.insert( valueType("Anna", 1) );


前面我们已经学会了,使用下标操作符统计输入的单词,
insert
成员函数同样可以实现:

[code]// count number of times each word occurs in the input
map<string, int> wordCount; // empty map from string to int
string word;
while ( cin >> word )
{
    // insert element with key equal to word and value 1
    // if word already in wordCount, insert does nothing
    pair<map<string, int>::iterator, bool> ret = 
        wordCount.insert(make_pair(word, 1));
    if ( !ret->second )       // word already in wordCount
        ++ ret.first->second; // increment counter
}


本处使用了带有一个键-值
pair
形参的
insert
版本将返回:包含一个迭代器和一个
bool
值的
pair
对象,其中迭代器指向
map
中具有相应键的元素,而
bool
值则表示是否插入了该元素。如果该键已在容器中,则其关联的值保持不变,返回的
bool
值为
false
;如果该键不在容器中,则插入新元素,且
bool
值为
true
。对于每个单词,都尝试
insert
它,并将它的值赋为1。
if
语句检测
insert
函数返回值,如果值为
false
则表示没有做插入操作;按照
word
索引的元素已在
wordCount
中存在,此时将该元素所关联的值加1。

4 查找并读取
map
元素

下标操作符给出了读取一个值的最简单方法:

[code]map<string, int> wordCount;   // empty map from string to int
int occurs = wordCount["Anna"];


但是,该方法存在一个副作用:如果该键不存在
map
容器中,则下标操作会插入一个具有该键的新的元素。大多数情况下,我们其实只是想知道某元素是否存在,当该元素不存在时,我们并不打算进行插入运算。为此,
map
容器提供的两个操作可以解决这一问题。

操作含义
m.count(k)
返回
m
k
的出现次数
m.find(k)
如果
m
容器中存在按
k
索引的元素,则返回指向该元素的迭代器。如果不存在,则返回指向超出末端迭代器
[code]// count()
int occurs(0);
if ( wordCount.count("Anna") )
    occurs = wordCount["Anna"];

// find()
int occurs(0);
map<string, int>::iterator iter = wordCount.find("Anna");
if ( iter != wordCount.end() )
    occurs = iter->second;


5 从
map
对象中删除元素

操作含义
m.erase(k)
删除
m
中键为
k
的元素。返回
size_type
类型的值,表示删除的元素的个数
m.erase(p)
m
中删除迭代器
p
所指向的元素。
p
必须指向
m
中确实存在的元素,而且不能等于
m.end()
。返回
void
类型
m.erase(b, e)
m
中删除一段范围内的元素,该范围由迭代器对
b
e
标记。
b
e
必须标记
m
中的一段有效范围:即
b
e
都要指向
m
中的元素或最后一个元素的下一个位置。并且,
b
e
要么相等(此时删除范围为空),要么
b
所指向的元素必须出现在
e
所指向的元素之前。返回
void
类型
[code]// erase of a key returns number of elements removed
if ( wordCount.erase( removalWord ) )
    cout << "ok: " << removalWord << " removed!" << endl;
else
    cout << "oops: " << removalWord << " not found!" << endl;


6 对
map
对象的迭代遍历

[code]// get iterator positioned on the first element
map<string, int>::const_iterator iter = wordCount.begin();

// for each element in the map
while ( iter != wordCount.end() )
{
    // print the element key, value pairs
    cout << iter->first << " occurs " << iter->second << " times." << endl;
    ++ iter; // increment iterator to denote the next element
}


参考文献:

《C++ Primer中文版(第四版)》,Stanley B.Lippman et al. 著, 人民邮电出版社,2013。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: