代码改变世界

C++Primer 第十一章 关联容器

2016-06-07 20:47  szn好色仙人  阅读(233)  评论(0编辑  收藏  举报
//1.关键容器支持高效的关键字查找和访问。
map                 关联数组:保存关键字-值对。通过关键字来查找值。
set                 关键字即值,即只保存关键字的容器。
multimap            关键字可重复出现的map
multiset            关键字可重复出现的set
//尚不研究无序关联容器
unordered_map       用哈希函数组织的map
unordered_set       用哈希函数组织的set
unordered_multimap  用哈希函数组织的map,关键字可重复出现
unordered_multiset  用哈希函数组织的set,关键字可以重复出现
//  multiply  ['mul·tiply]v.  乘
//  当初始化一个map的时候,必须提供关键字类型和值类型。
//  对于有序关联容器关键字类型必须定义元素比较的方法。有序关联容器都是默认按照<进行排序。
//  有multi前缀的关联容器的关键字是可以重复的,否则此关联容器的关键字都是不能重复的
//  不能改变关联容器的关键字部分,这就推出set系列的关联容器的元素不能被改变,map系列的关联容器的元素的first成员不能被改变。
//  关联容器默认使用关键字的<操作来比较关键字。

//2.关联容器的迭代器都是双向迭代器,不支持随机访问,这就意味着关联容器的迭代器不支持 += -= 等操作(包括map和unordered_map),除了map和unordered_map以外的关联容器也不能支持下标运算符操作。

//3.pair:定义在头文件utility中,当创建一个pair的时候必须提供两个类型名。
//  pair定义的操作:
//     A:make_pair:定义在命名空间std中,返回一个用传入值初始化(构造)的pair。
//     B:first:返回pair中名为first的数据成员。
//     C:second:返回pair中名为second的数据成员。
//     == != :当first和second成员均相等的时候,两个pair相等。相等性由元素的==运算符判断。
//  utility  [u·til·i·ty]n. 公用程序

//4.关联容器的额外类型别名:(关联容器含有顺序容器中定义的类型别名)
//  key_type:关键字类型
//  mapped_type:每个关键字关联的类型,只适用于map系列的关键容器
//  value_type:对于set系列的关键容器,与key_type相同,对于map系列的关键容器,为pair<const key_type, mapped_type>

//5.当解引用关联容器的迭代器的时候,会得到一个类型为容器的value_type的值的引用。

//6.由于关联容器的读写限制,和其迭代器不支持随机访问,所以一般不对关联容器使用泛型算法,而是使用其本身的算法。
//  若是对关联容器使用泛型算法,也一般将其当做源序列或者是目的位置。

//7.对关联容器添加元素:使用insert和emplace
map<string, int> StrInt;
StrInt.insert(make_pair("1", 1));
StrInt.insert(make_pair("0", 0));
StrInt.insert(make_pair("3", 3));
//insert在插入单一元素的时候返回一个pair,其first成员是插入的元素,其second成员是插入的结果,成功为true,失败为false
//insert插入的时候,可以指定从哪里开始搜索插入的位置,对插入结果没有影响,应该会影响插入的效率
auto temValue = StrInt.insert(StrInt.begin(), make_pair("2", 2));    
//StrInt = [4](("0", 0),("1", 1),("2", 2),("3", 3))
//temValue = (("2", 2), true)

map<string, int> StrInt1;
//insert在插入一个序列的时候,返回void
StrInt1.insert(StrInt.begin(), StrInt.end());
//StrInt1 = [4](("0", 0),("1", 1),("2", 2),("3", 3))

map<string, unique_ptr<char[]>> StrChar;
StrChar.emplace(make_pair("1", new char[1]));
//insert是按值插入,emplace是构造插入

//8.关联容器删除元素:
map<string, int> StrInt;
StrInt.insert(make_pair("1", 1));
StrInt.insert(make_pair("0", 0));
StrInt.insert(make_pair("3", 3));
StrInt.insert(StrInt.end(), make_pair("2", 2));                    //StrInt = [4](("0", 0),("1", 1),("2", 2),("3", 3))
map<string, int>::size_type value = StrInt.erase(string("0"));     //value = 1 StrInt = [3](("1", 1),("2", 2),("3", 3))
//erase:第一个版本,删除关联容器中每个关键字为传入参数的元素,返回删除元素的数量

StrInt.erase(StrInt.begin());                                      //StrInt = [2](("2", 2),("3", 3))
//erase:第二个版本,删除关联容器中指定迭代器所指的元素

StrInt.erase(StrInt.begin(), StrInt.end());                        //StrInt = [0]()
//erase:第三个版本,删除关联容器中指定迭代器序列所指的元素

//9.map和unordered_map容器提供了下标运算符和at()成员函数,通过传入关键字来得到与关键字绑定的值。注意点:当关键字不在指定的容器中的时候,下标运算符会为其创建一个值初始化的值与关键字绑定并插入指定容器,而使用at()的时候则会报out_of_range的错误。
//  由于下标运算符可能插入一个新元素,所以只能对非const的map或者unordered_map进行下标操作。
//  通常情况下,一个容器的下标的返回值和其迭代器解引用的返回值的类型是一致的,但是对于map和unordered_map来说,下标运算符返回类型是mapped_type,解引用迭代器返回类型是value_type
//  此处的下标运算符返回的是左值,所以可以读写元素。

//10.关联容器中的查找操作:
c.find(k):返回一个迭代器,指向第一个关键字为k的元素,若k不在容器中,则返回尾后迭代器
c.count(k):返回关键字等于k的元素的数量。
c.lower_bound(k):返回一个迭代器,指向第一个关键字不小于k的元素。换句话说,此函数返回的迭代器指向第一个具有给定关键字的元素。
c.upper_bound(k):返回一个迭代器,指向第一个关键字大于k的元素。换句话说,此函数返回的迭代器指向最后一个匹配给定关键字的元素之后的元素。
c.equal_range(k):返回一个迭代器pair,若关键字存在,则第一个迭代器指向第一个与关键字匹配的元素,第二个迭代器指向最后一个匹配元素之后的位置。若未找到元素,则两个迭代器都指向关键字可以插入的位置。
//当给定的关键字不在序列中的时候,lower_bound和upper_bound返回值相同,返回关键字的第一个安全插入点,即不影响容器中元素顺序的插入位置。

  int _tmain(int argc, _TCHAR* argv[])
  {
      const int CCount = 1e6;
      const int CTime = 50;
      vector<int> vecInt;

      unsigned long long nT0 = GetTickCount64();
      for (int i = 0; i < CCount; ++i)
      {
       vecInt.emplace_back(i);
      }
      unsigned long long nT1 = GetTickCount64() - nT0; //nT1 = 124

      map<int, int> mapInt;
      unsigned long long nT2 = GetTickCount64();
      for (int i = 0; i < CCount; ++i)
      {
          mapInt.emplace(make_pair(i, i));
      }
      unsigned long long nT3 = GetTickCount64() - nT2; //nT3 = 3869

      unsigned long long t0 = GetTickCount64();
      for (int i = 0; i < CTime; ++i)
      {
          find_if(vecInt.begin(), vecInt.end(), [CCount](int &i){return i == CCount - 1;});
      }
      unsigned long long t1 = GetTickCount64() - t0; //t1 = 546

      unsigned long long t2 = GetTickCount64();
      for (int i = 0; i < CTime; ++i)
      {
         find_if(mapInt.begin(), mapInt.end(), [CCount](map<int, int>::value_type &v){return v.second == CCount - 1;});
      }
      unsigned long long t3 = GetTickCount64() - t2; //t3 = 6271

      unsigned long long t4 = GetTickCount64();
      for (int i = 0; i < CTime; ++i)
      {
          mapInt.find(CCount - 1);
      }
      unsigned long long t5 = GetTickCount64() - t4; //t5 = 0
  }

  //map会维护插入元素的顺序,所以插入慢,但是使用map本身的find来查找却非常快

//11.关联容器支持通过关键字查找和提取元素。对关键字的使用将关联容器和顺序容器区分开,顺序容器是通过位置访问元素。