关联容器操作

Posted xiaojianliu

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了关联容器操作相关的知识,希望对你有一定的参考价值。

技术图片

  • 对于 set 类型,key_typevalue_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;   //正确,可以通过迭代器改变元素的值

注意:

一个 mapvalue_type 是一个 pair 类型,可以改变 pair 的值,但是不能改变关键字成员的值。

set 的迭代器是 const 的

虽然 set 同时定义了关键字 iteratorconst_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中的值是只读的
}

历关联容器

mapset 都支持 beginend 操作,可以使用这些函数获取迭代器,然后使用迭代器来遍历容器。

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;
}

注意:

当使用一个迭代器遍历一个 mapmultimapsetmultiset 时,迭代器按关键字升序遍历元素。

关联容器和算法

通常不会对关联容器使用泛型算法。

关键字 const 的特性意味着不能讲关联容器传递给修改元素或者重排元素的算法,而 set 的关键字是 const的,map 的键是 const 的。

关联容器可以用于只读算法,但是这类算法通常都要搜索序列,由于关联容器中的元素不能通过它们的关键字进行快速查找,因此对其使用泛型算法几乎总是个坏主意。

在实际编程时,要对关联容器使用算法的情况:

  • 把关联容器当作一个源序列。可以使用泛型算法 copy 将元素从一个关联容器拷贝到另一个序列。
  • 把关联容器当作一个目的位置。可以调用 insert 将一个插入器绑定到一个关联容器,通过使用 insert,可以将关联容器当作一个目的位置来调用另一个算法。

添加元素

技术图片

insert 向容器中添加一个元素或一个元素范围。

mapset (以及对应的无序类型)包含不重复的关键字,因此插入一个已存在的元素对容器没有任何影响。

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 的返回值

insertemplace 的返回值依赖于容器类型和参数。对于不包含重复关键字的容器,添加单一元素的额insertemplace 版本返回一个 pair

  • pairfirst 成员是一个迭代器,只向具有给定关键字的元素。
  • pairsecond 成员是一个 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.firstpair 的第一个成员,是一个 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 的下标操作

技术图片

  • mapunordered_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 中查找元素

如果一个 multinmapmultiset 中有多个元素具有给定的关键字,则这些元素在容器中会相邻存储。

查找给定作者的著作:

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_boundupper_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_boundupper_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及其查找操作

Android:使用 putExtra 从片段访问容器活动对象?

关联容器操作

在带有操作栏的活动中使用时,片段未完全显示

在带有操作栏的活动中使用时,片段未完全显示

EF添加关联的提示问题:映射从第 260 行开始的片段时有问题: