• c++基础(五)——关联容器


    1.关联容器

      关联容器中的元素时按照关键字来保存和访问的,与之相对的,顺序容器中的元素时按它们在容器中的位置来顺序保存和访问的。两个主要关联容器是 map 和 set。标准库提供了8个关联容器,这8个容器间的不同体现在
    三个维度上:

    • 或是一个 map 或是一个 set
    • 或者要求不重复关键字,或者允许重复关键字
    • 按顺序保存元素,或无序保存

    2.使用关联容器

    • map
    • 1 //统计每个单词在输入中出现的次数
      2 map<string, size_t> word_count;    //string到size_t的空map
      3 string word;
      4 while (cin >> word)
      5     ++word_count[word];    //提取word的计数器并将其加1
      6 for(const auto &w : word_count)    //对map中的每个元素
      7     //打印结果
      8     cout << w.first << " occurs " << w.second
      9          << ((w.second > 1)? " times ": "time") << endl;
    • set
    • 1 //统计输入中每个单词出现的次数
      2 map<string, size_t> word_count;    //string到size_t的空map
      3 set<string> exclude = {"The", "But", "And", "Or", "An", "A",
      4                "the", "but", "and", "or", "an", "a"};
      5 string word;
      6 while (cin >> word)
      7     //只统计不在exclude中的单词
      8     if (exclude.find(word) == exclude.end())
      9         ++word_count[word];    //获取并递增word的计数器

    3.关联容器概述

      关联容器不支持顺序容器的位置相关操作,例如push_front或push_back。原因是关联容器中元素是根据关键字存储的,这些操作对关联容器没有意义。关联容器的迭代器都是双向的

    • 初始化 multimap 和 multiset。一个map或set中的关键字必须是唯一的,容器multimap和multiset没有此限制,它们允许多个元素具有相同的关键字。
    •  1 //定义一个有20个元素的vector,保存0到9每个整数的两个拷贝
       2 vector<int> ivec;
       3 for (vector<int>::size_type i = 0; i != 10; ++i){
       4     ivec.push_back(i);
       5     ivec.push_back(i);    //每个数重复保存一次
       6 }
       7 //iset包含来自ivec的不重复的元素,miset包含所有20个元素
       8 set<int> iset(ivec.begin(), ivec.end());
       9 multiset<int> miset(ivec.begin(), ivec.end());
      10 cout << ivec.size() << endl;    //打印出20
      11 cout << iset.size() << endl;    //打印出10
      12 cout << miset.size() << endl;    //打印出20

     3.1 set,map自定义函数排序

      关联容器对其关键字类型有一些限制。对于有序容器,关键字类型必须定义元素比较的方法。默认情况下,标准库使用关键字类型<运算符来比较两个关键字。

      用来组织一个容器中元素的操作的类型也是该容器类型的一部分。为了指定使用自定义的操作,必须在定义关联容器类型时提供此操作的类型使用方法如下:用尖括号指定要定义的那种类型的容器,自定义的操作类型必须在尖括号中紧跟元素类型给出。例如:我们不能直接定义一个Sales_data的multiset,因为Sales_data没有<运算符。因此需要定义一个compareIsbn:

    1 bool compareIsbn (const Sales_data &lhs, const Sales_data &rhs)
    2 {
    3     return lhs.isbn() < rhs.isbn();
    4 }

      为了使用自己定义的操作,在定义multiset时我们必须提供两个类型:关键字类型Sales_data,以及比较操作类型——应该是一种函数指针类型,可以指向compareIsbn。

    1 //bookstore中多条记录可以有相同的ISBN
    2 //bookstore中的元素以ISBN的顺序进行排列
    3 multiset<Sales_data, decltype(compareIsbn)*> bookstore(compareIsbn);

      此处,我们使用decltype来指出自定义操作的类型。记住,当用decltype来获得一个函数指针类型时,必须加上一个*来指出我们要使用一个给定函数类型的指针;用compareIsbn来初始化bookstore对象,这表示当我们想bookstore添加元素时,通过调用compareIsbn来为这些元素排序。

      下面举两个实例:均按照降序进行排列,容器默认是使用“<”。

     1 #include <set>
     2 #include <map>
     3 #include <vector>
     4 #include <string>
     5 #include <iostream>
     6 using namespace std;
     7 
     8 class st {
     9 public:
    10     int id;
    11     string name;
    12     st(int id, string name);
    13     ~st() {};
    14 };
    15 
    16 st::st(int id, string name) :id(id), name(name) {
    17 
    18 }
    19 
    20 bool compareST(const int &s, const int &r) {
    21     return s > r;
    22 }
    23 
    24 bool compareST2(const st *s, const st *r) {
    25     return s->id > r->id;
    26 }
    27 
    28 int main()
    29 {
    30     /// map
    31     st *st1 = new st(3, "st1");
    32     st *st2 = new st(1, "st2");
    33     st *st3 = new st(2, "st3");
    34     
    35     map<int, st*, decltype(compareST)*> temp(compareST);
    36     temp.insert(make_pair(st1->id, st1));
    37     temp.insert(make_pair(st2->id, st2));
    38     temp.insert(make_pair(st3->id, st3));
    39 
    40     for (auto item : temp) {
    41         cout << item.first << "	" << item.second->name << endl;
    42     }
    43 
    44     cout << "========================" << "
    ";
    45 
    46     /// set
    47     set<st*, decltype(compareST2)*> temp2(compareST2);
    48     temp2.insert(st1);
    49     temp2.insert(st2);
    50     temp2.insert(st3);
    51 
    52     for (auto item : temp2) {
    53         cout << item->id << "	" << item->name << endl;
    54     }
    55     
    56     getchar();
    57     return 0;
    58 }

    3.2 pair 类型

      它定义在头文件utility中。类似容器,pair是一个用来生成特定类的模板,与其他标准库类型不同,pair的数据成员是public的。两个成员分别命名为firstsecond,我们用普通的成员访问符号来访问它们。

      如下是一个错误写法:

    1 map<int, string> temp;
    2 pair<int, string> p(); // 显得不伦不类了。①不加括号,即采用第一种进行值初始化②采用(1,"test")利用第二种方式初始化
    3 temp.insert(p);

      改正:

    1 map<int, string> temp;
    2 pair<int, string> p(1, "dd");
    3 pair<int, string> p2;
    4 temp.insert(p);
    5 temp.insert(p2);
    6 temp.insert(make_pair(1, "dd"));

    3.3 关联容器的操作

      关联容器额外的类型别名:

      对于 set 类型,key_type 和 value_type 是一样的:set 中保存的就是关键字。

      在 map 中,元素是关键字-值对,即每一个元素是一个 pair 对象,包含一个关键字和一个关联的值,同时因为不能改变一个元素的关键字,因此这些 pair 的关键字部分是 const 的。

    1 map<int, string>::key_type v; // v 是一个 int
    2 map<int, string>::mapped_type v2; // v 是一个 string
    3 set<string>::key_type s; // s 是一个 string
    4 set<string>::value_type s2; // s 是一个 string 
    5 map<int, string>::value_type v3;// 是一个 pair<int, string> 类型

     4. 关联容器迭代器

      map 中的 key 值是const的,不能够被改变。

    1 map<int, string> m;
    2 m.insert({ 1, "a" });
    3 m.insert({ 2, "b" });
    4 for (auto iter = m.begin(); iter != m.end(); ++iter) {
    5    cout << iter->first << iter->second<<endl;
    6 }

      set的迭代器是const的。set 中的值 也只能访问不能修改。

    4.1 添加元素

      由于map和set包含不重复的关键字,因此插入一个已存在的元素对容器没有任何影响:

      注:insert有两个版本分别接受一对迭代器,或是一个初始化器列表,这两个版本的行为类似对应的构造函数。

    1 vector<int> ivec = { 2, 4, 6, 8, 2,4, 6, 8 };
    2 set<int> set2;
    3 set2.insert(ivec.begin(), ivec.end());    //set2有4个元素

    4.1.1 向 map 添加元素

    1 //向word_count插入word的4中方法
    2 word_count.insert({word, 1}); // 最简单
    3 word_count.insert(make_pair(word, 1));
    4 word_count.insert(pair<string, size_t>(word, 1));
    5 word_count.insert(map<string, size_t>::value_type(word, 1)); //构造一个恰当的 pair 类型,并构造该类型的一个对象,插入到map中

    4.1.2 检测 insert 的返回值

      insert(或emplace)返回的值依赖于容器类型和参数。对于不包含重复关键字的容器,添加单一元素的insert和emplace版本返回一个pair,告诉我们插入操作是否成功pair的first成员是一个迭代器,指向具有给定关键字的元素;second成员是一个bool值,指出元素是插入成功还是已经存在于容器中。

    4.1.3 删除元素

      关联容器定义了三个版本的erase,与顺序容器一样,我们可以通过传递给erase一个迭代器或一个迭代器对来删除一个元素或一个元素范围;关联容器还提供一个额外的erase操作,它接受一个key_type参数。

    4.1.4 map 的下标操作

      map和unordered_map支持下标运算和一个对应的 at 函数,但是 set 不支持,因为set中本身存放的就是 关键字。

     

      与其他下标运算符不同的是,但是如果关键字并不在map中,会为它创建一个元素并插入到map中,关联值将进行初始化。比如下面的例子:

    1     map<int, int> m;
    2     m.insert({ 1, 1 });
    3     m.insert({ 2, 2 });
    4 
    5     cout << m.size() << endl; // 2
    6     cout << m[3] << endl; // 0. key=3 的值不存在,插入并初始化
    7     cout << m.size() << endl; // 3
    8     cout << m[3] << endl; // 0    

     4.2 访问元素

      如果我们所关心的只不过是一个特定元素是否在容器中,可能find是最佳选择。

    1 set<int> iset = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
    2 iset.find(1);    //返回一个迭代器,指向key==1的元素
    3 iset.find(11);    //返回一个迭代器,其值等于iset.end()
    4 iset.count(1);    //返回1
    5 iset.count(11);    //返回0

      

      map 中利用 find 代替下标操作。当我们不想改变 map 的时候,下标操作会带来麻烦。

    1 if (word_count.find("foobar") == word_count.end())
    2     cout << "foobar is not in the map " << endl;

      在multimap或multiset中查找元素。如果 multimap 中有多个元素具有给定的关键字,这些元素会在容器中相邻存储

    1 string search_item("Alain de Botton");    //要查找的作者
    2 auto entries = authors.count(search_item);    //元素的数量
    3 auto iter = authors.find(search_item);    //此作者的第一本书
    4 //用一个循环查找此作者的所有著作
    5 while(entries){
    6     cout << iter->second << endl;    //打印每个题目
    7     ++iter;                //前进到下一本书
    8     --entries;            //记录已经打印了多少本书
    9 }

      

  • 相关阅读:
    SharePoint 2013 配置基于表单的身份认证
    SharePoint 2013 场解决方案包含第三方程序集
    SharePoint 2010 站点附加数据升级到SP2013
    SharePoint 2013 在母版页中插入WebPart
    SharePoint 2013 搭建负载均衡(NLB)
    SharePoint 部署解决方案Feature ID冲突
    SharePoint 2013 配置基于AD的Form认证
    SharePoint Server 2016 Update
    SharePoint 2013 为用户组自定义EventReceiver
    SharePoint 2013 JavaScript API 记录
  • 原文地址:https://www.cnblogs.com/KongHuZi/p/11366383.html
Copyright © 2020-2023  润新知