1.使用关联容器
使用map
1 | map<string, size_t> word_count; |
使用set
1 | map<string, size_t> word_count; |
2.概述
关联容器不支持顺序容器的位置相关的操作,例如push_front push_back。而且关联容器也不支持构造函数或插入函数这些接受一个元素值和一个数量值的操作。
关联容器的迭代器都是双向的。
2.1 定义关联容器
可以创建一个指定类型的空容器,也可以将关联容器初始化为另一个同类型容器的拷贝,或是从一个值范围来初始化关联容器,只要这些值可以转化为容器所需类型就可以。
1 | map<string, size_t> word_count; |
当初始化一个map时,必须提供关键字类型和值类型。
multimap和multiset允许多个元素具有相同的关键字。
1 | vector<int> ivec; |
2.2 关键字类型的要求
对于有序容器map、multimap、set、multiset,关键字类型必须定义元素比较的方法。默认情况下,使用<运算符比较两个关键字。
使用关键字类型的比较函数
1 | bool compareIsbn(const Sales_data &lhs, const Sales_data &rhs) |
为了定义自己的操作,在定义multiset时必须提供两个类型:关键字类型Sales_data,以及比较操作类型-应该是一种函数指针类型,可以指向compareIsbn。当定义次容器类型的对象时,需要提供想要使用的操作的指针。
1 | multiset<Sales_data,decltype(compareIsbn)*> bookstore(compareIsbn); |
添加元素时,通过调用compareIsbn来为这些元素排序。
1 | bool less(int lhs, int rhs) |
2.3 pair类型
定义在utility中。一个pair保存两个数据成员。pair是一个用来生成特定类型的模板,当创建一个pair时,
1 | pair<string,string> anon; |
pair的默认构造函数对数据成员进行值初始化。
pair<string, string> author{"James","Joyce"};
pair的数据成员是public的,分别为first和second。
cout<<w.first<<" occurs "<<w.second<<((w.second > 1)?" times":" time")<<endl;
语法 | 解释 |
---|---|
pair< T1,T2 > p; | p是一个pair,两个类型分别为T1和T2的成员都进行了值初始化 |
pair< T1,T2 > p(v1, v2) | p是一个类型分别为T1和T2的pair;first和second成员分别用v1和v2进行初始化 |
pair< T1,T2 > p = {v1, v2} | 等价于p(v1, v2) |
make_pair<v1,v2) | 返回一个用v1和v2初始化的pair。pair的类型从v1和v2的类型推断出来 |
p.first | 返回p的名为first的数据成员 |
p.second | 返回p的名为second的数据成员 |
p1 relop p2 | 关系运算符(< > <= >=)按字典序定义。例如,当p1.first < p2.first 或!(p2.first < p1.first) && p1.second < p2.second成立时,p1 < p2为true。关系运算符利用元素的<运算符实现 |
p1 == p2 | 当first和second成员分别相等时,两个pair相等。相等性判断利用元素的==运算符实现 |
p1 != p2 |
创建pair对象的函数
1 | pair<string, int> process(vector<string> &v) |
也可以使用make_pair。
1 | if(!v.empty()) |
3 关联容器操作
语法 | 解释 |
---|---|
key_type | 此容器类型的关键字类型 |
mapped_type | 每个关键字关联的类型:只适用于map |
value_type | 对于set,与key_type相同;对于map,为pair |
对于set类型,key_type和value_type是一样的。在一个map中,元素是关键字-键对。由于我们不能改变一个元素的关键字,因此这些pair的关键字部分是const的。
1 | set<string>::value_type v1; //v1是一个string |
使用作用域运算符来踢去一个类型的成员。只有map类型才定义了mapped_type。
3.1 关联容器迭代器
解引用一个关联容器迭代器时,会得到一个类型为容器的value_type的值的引用。对map而言,value_type是一个pair类型,其first成员保存const的关键字,second成员保存值。
1 | auto map_it = word_count.begin(); |
一个map的value_type是一个pair,我们可以改变pair的值,但不能改变关键字成员的值。
set的迭代器是const的
与不能改变一个map元素的关键字一样,一个set中的关键字也是const的。
1 | set<int> iset = {0,1,2,3,4,5,6,7,8,9}; |
遍历关联容器
1 | 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时,迭代器按关键字升序遍历元素。
我们通常不对关联容器使用泛型算法。
3.2 添加元素
1 | vector<int> ivec = {2,4,6,8,2,4,6,8}; |
检测insert的返回值
依赖于容器类型和参数。对于不包含重复关键字的容器,添加单一元素的insert和emplace版本返回一个pair,告诉我们插入操作是否成功。pair的first成员是一个迭代器,指向具有给定关键字的元素;second成员是一个bool值,指出元素是否插入成功。
1 | map<string, size_t> word_count; string word; while (cin >> word) { auto ret = word_count.insert({word, 1}); //如果word已存在word_count中,insert什么也不做 if (!ret.second) |
3.3 删除元素
三个版本:erase一个迭代器、一个元素范围,指定的元素被删除,返回void。接受一个key_type参数,删除所有匹配给定关键字的元素,返回实际删除的元素的数量。
1 | if (word_count.erase(removal_word)) cout << "ok: " << removal_word << " removed\n"; else cout << "oops: " << removal_word << " not found!\n"; |
语法 | 解释 |
---|---|
c.erase(k) | 从c中删除每个关键字为k的元素。返回一个size_type值,指出删除的元素的数量 |
c.erase(p) | 从c中删除迭代器p指向的元素。p必须指向c中一个真实元素,不能等于c.end()。返回一个指向p之后的元素的迭代器,若p指向c中的尾元素,则返回c.end() |
c.erase(b,e) | 删除迭代器对b和e所表示的范围中元素。返回e |
3.4 下标操作
map和unordered_map容器提供下标运算符和一个对应的at函数。set不支持。multimap和unordered_multimap也不支持。
1 | map<string, size_t> word_count; |
set
iset.find(1); //返回一个迭代器,指向key==1的元素
iset.find(11); //返回一个迭代器,
iset.count(1);
iset.count(11);1
2
对map使用find代替下标操作,下标运算符可能会插入新元素。
if (word_count.find(“foobar”) == word_count.end())
cout << “foobar is not in the map” << endl;1
2
在multimap或multiset中查找元素
string search_item(“Alain de Botton”); //
authors.count(search_item);
authors.find(search_item);
while(entries) {
cout << iter->second << endl;
–entries;
}1
2
一种不同的,面向迭代器的解决方法
for(auto beg = authors.lower_bound(search_item),end=authors.upper_bound(search_item); beg != end; ++beg)
cout<1
2
3
4
5lower_bound返回的迭代器可能指向一个具有给定关键字的元素,但也可能不指向。如果关键字不在容器中,则lower_bound会返回关键字的第一个安全插入点-不影响容器中元素顺序的插入位置。
如果lower_bound和upper_bound返回相同的迭代器,则给定关键字不在容器中。
equal_range函数
返回pair,first成员保存的迭代器与lower_bound返回的迭代器一样;second保存的迭代器与upper_bound返回的迭代器一样。
for(auto pos = authors.equal_range(search_item); pos.first != pos.second;++pos.first)
cout<1
2
3
4
5
4 无序容器
新标准定义了4个无序关联容器,使用哈希函数和关键字类型的==运算符。
无序容器在存储上组织为一组桶,每个桶保存零个或多个元素。无序容器使用哈希函数将元素映射到桶。
// unordered_set::bucket_count
#include
#include
#include
int main ()
{
std::unordered_set
{“Mercury”,”Venus”,”Earth”,”Mars”,”Jupiter”,”Saturn”,”Uranus”,”Neptune”};
unsigned n = myset.bucket_count();
std::cout << “myset has “ << n << “ buckets.\n”;
for (unsigned i=0; i<n; ++i) {
std::cout << “bucket #” << i << “ contains:”;
for (auto it = myset.begin(i); it!=myset.end(i); ++it)
std::cout << “ “ << *it;
std::cout << “\n”;
}
return 0;
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19输出:
myset has 11 buckets.
bucket #0 contains:
bucket #1 contains: Venus
bucket #2 contains: Jupiter
bucket #3 contains:
bucket #4 contains: Neptune Mercury
bucket #5 contains:
bucket #6 contains: Earth
bucket #7 contains: Uranus Saturn
bucket #8 contains: Mars
bucket #9 contains:
bucket #10 contains:
对关键字类型的要求
定义关键字类型为自定义类类型的无序容器,需要提供自己的hash模板和==运算符。
size_t hasher(const Sales_data &sd)
{
return hash
}
bool eqOp(const Sales_data &lhs, const Sales_data &rhs)
{
return lhs.isbn() == rhs.isbn();
}
using SD_multiset = unordered_multiset
SD_multiset bookstore(32,hasher,eqOp);
```