关联容器操作
Posted xiaojianliu
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了关联容器操作相关的知识,希望对你有一定的参考价值。
- 对于
set
类型,key_type
和value_type
是一样的,set
中保存的值就是关键字。 - 在一个
map
中,元素时关键字-值对,即,每个元素都是一个pair
对象,包含一个关键字和一个关联的值,由于我们不能改变一个元素的关键字,因此这些pair
的关键字部分是const
的。
set<string>::value_type v1; //v1是string
set<string>::key_type v2; //v2是string
map<string, int>::value_type v3; //v3是pair<const string,int>
map<string, int>::key_type v4; //v4是string
map<string, int>::mapped_type v5; //v5是int
注意:
只有 map
类型(unordered_map、unordered_multimap、multimap、map
)才定义了 mapped_type
类型。
关联容器迭代器
解引用一个关联容器将得到一个类型为容器的 value_type
的值得引用。
map<string, size_t> word_count;
auto map_it = word_count.begin();
cout << map_it->first;
cout << map_it->second;
map_it->first = "new key"; //错误,关键字是const类型
++map_it->second; //正确,可以通过迭代器改变元素的值
注意:
一个 map
的 value_type
是一个 pair
类型,可以改变 pair
的值,但是不能改变关键字成员的值。
set 的迭代器是 const 的
虽然 set
同时定义了关键字 iterator
和 const_iterator
类型,但两种类型都只允许访问 set
的元素,不允许改变,即,set
的关键字是 const
类型的。
set<int> iset = { 0,1,2,3,4,5,6 };
set<int>::iterator set_it = iset.begin();
if (set_it != iset.end())
{
*set_it = 42; //错误,set中的值是只读的
}
历关联容器
map
和 set
都支持 begin
和 end
操作,可以使用这些函数获取迭代器,然后使用迭代器来遍历容器。
map<string, size_t> word_count;
auto map_it = word_count.cbegin();
while (map_it != word_count.cend())
{
cout << map_it->first << " occurs " << map_it->second << " times" << endl;
++map_it;
}
注意:
当使用一个迭代器遍历一个 map
、multimap
、set
、multiset
时,迭代器按关键字升序遍历元素。
关联容器和算法
通常不会对关联容器使用泛型算法。
关键字 const
的特性意味着不能讲关联容器传递给修改元素或者重排元素的算法,而 set
的关键字是 const
的,map
的键是 const
的。
关联容器可以用于只读算法,但是这类算法通常都要搜索序列,由于关联容器中的元素不能通过它们的关键字进行快速查找,因此对其使用泛型算法几乎总是个坏主意。
在实际编程时,要对关联容器使用算法的情况:
- 把关联容器当作一个源序列。可以使用泛型算法
copy
将元素从一个关联容器拷贝到另一个序列。 - 把关联容器当作一个目的位置。可以调用
insert
将一个插入器绑定到一个关联容器,通过使用insert
,可以将关联容器当作一个目的位置来调用另一个算法。
添加元素
insert
向容器中添加一个元素或一个元素范围。
map
和 set
(以及对应的无序类型)包含不重复的关键字,因此插入一个已存在的元素对容器没有任何影响。
vector<int> ivec = { 2,4,6,8,2,4,6,8 };
set<int> iset;
iset.insert(ivec.cbegin(), ivec.cend()); //iset包含4个元素
iset.insert({ 1,3,5,7,1,3,5,7 }); //iset包含8个元素
向 map 添加元素
对一个map insert时候,需要记住元素类型是 pair,通常需要在insert的参数类别中创建一个pair:
map<string, size_t> word_count;
word_count.insert({ "word",1 });
word_count.insert(make_pair("word", 1));
word_count.insert(pair<string,size_t>("word",1));
word_count.insert(map<string, size_t>::value_type("word", 1));
检测 insert 的返回值
insert
或 emplace
的返回值依赖于容器类型和参数。对于不包含重复关键字的容器,添加单一元素的额insert
和 emplace
版本返回一个 pair
:
pair
的first
成员是一个迭代器,只向具有给定关键字的元素。pair
的second
成员是一个bool
值,指出元素是插入成功还是已经存在于容器中,如果关键字已经在容器中,则insert
什么也不做,且返回值中的bool
部分为false
。如果关键字不存在,元素被插入,且bool
返回true
。
map<string, size_t> word_count;
string word;
while (cin >> word)
{
auto ret = word_count.insert({ word, 1 });
if (!ret.second)
++ret.first->second; //递增计数值
}
ret
保存了insert
的返回值,是一个pair
类型。ret.first
是pair
的第一个成员,是一个map
迭代器,指向具有给定关键字的元素。ret.first->
解引用此迭代器,提取map
中的元素,元素也是一个pair
。ret.first->second map
中元素的值部分。- 实际上 ret 声明和初始化方式为:
pair<map<string, size_t>::iterator, bool> ret = word_count.insert(make_pair<word,1>);
向 multiset 或 multimap 中添加元素
multi
容器中的关键字不必唯一,因此在这些类型上的调用 insert
总会插入一个元素。
multimap<string, string> authors;
//插入一个元素,关键字是:Barth,Join
authors.insert({ "Barth,Join","Sot-Weed Factor" });
//插入一个元素,关键字还是:Barth,Join
authors.insert({ "Barth,Join","Lost in the Funhouse" });
对于允许重复关键字的容器,接受单个元素的 insert
操作返回一个指向新元素的迭代器,这里不需要返回bool
值,因为总是可以插入元素。
删除元素
关联容器提供了三个版本的 erase:
- 传递给
erase
一个迭代器来删除一个元素,返回void
。 - 传递给
erase
一个迭代器对来删除一个元素范围,返回void
。 - 传递给
erase
一个key_type
参数,删除所有匹配关键字的元素(如果存在的话),它返回实际删除的元素的数量。- 对于保存不重复关键字的容器,
erase
总是返回0或者1。 - 对于可重复关键字的容器,
erase
可能返回大于1。
- 对于保存不重复关键字的容器,
map 的下标操作
map
和unordered_map
容器提供了下标运算符和一个对应的at
函数。set
不支持下标,因为set中没有与关键字关联的值。- 不能对一个
multimap
或一个unordered_multimap
进行下标操作,因为这些容器中可能有多个值与关键字关联。
map
下标运算符接受一个索引,即一个关键字,获取与此关键字相关联的值。如果关键字不在 map
中,会为它创建一个元素病插入到 map
中,关联值则进行值初始化。
map<string, size_t> word_count; //空的map
word_count["Anna"] = 1;
执行的操作为:
- 在
word_count
搜索关键字为Anna
的元素,没有找到。 - 将一个新的关键字-值对插入到
word_count
中,关键字是一个const string
,保存Anna
,值进行值初始化,在此意味着0。 - 提取出新插入的元素,将值1赋予它。
访问元素
关联容器提供了多种查找一个指定元素的方法:
如果只关心一个特定元素是否已在容器中,使用 find
是最佳的选择,对于关键字不允许重复的容器,使用 find
和使用 count
没什么区别,但是对于允许关键字重复的容器,count
可以统计多少个元素有相同的关键字,当然如果不需要计数,则可以直接用 find
。
在 multimap 或 multiset 中查找元素
如果一个 multinmap
或 multiset
中有多个元素具有给定的关键字,则这些元素在容器中会相邻存储。
查找给定作者的著作:
string search_item("Alain de Botton"); //指定作者
auto entries = authors.count(search_item); //元素的数量
auto iter = authors.find(search_item); //此作者的第一本书
while(entries){
cout<<iter->second<<endl;
++iter;
--entries;
}
一种不同的,面向迭代器的解决办法
lower_bound
和 upper_bound
可以获取具有相同关键字的一个迭代器范围。这两个操作有可能返回的迭代器是尾后迭代器:
- 如果查找的元素具有容器中最大的关键字,则此关键字的
upper_bound
返回尾后迭代器。 - 如果关键字不存在,且大于容器中任何关键字,则
lower_bound
返回的也是尾后迭代器。
注意:
lower_bound
返回的迭代器可能指向一个具有给定关键字的元素,但也可能不指向。如果关键字不在容器中,则lower_bound
会返回关键字的第一个安全插入点---- 不影响容器中元素顺序的插入位置。
重写上面的操作:
for (auto beg = authors.lower_bound(search_item), beg = authors.upper_bound(search_item);
beg != end; ++beg)
cout << beg->second << endl;
注意:
如果 lower_bound
和 upper_bound
返回相同的迭代器,则给定关键字不在容器中。
equal_range 函数
equal_range
函数接受一个关键字,返回一个迭代器 pair
,若关键字存在,则第一个迭代器指向第一个与关键字匹配的元素,第二个迭代器指向最后一个匹配关键字之后的位置。如果没有找到匹配的元素,则两个迭代器都指向关键字可以插入的位置。
再次修改上面的程序:
for(auto pos = authors.equal_range(search_item);
pos.first != pos.second;++pos.first_
cout<< pos.first->second<<endl;
以上是关于关联容器操作的主要内容,如果未能解决你的问题,请参考以下文章
C++ STL|深入理解关联容器multimap和map及其查找操作