C++ Primer第五版笔记——关联容器

一、类型
关联容器支持高效的关键字查找和访问,标准库中两个主要的关联容器是map和set。map中的元素是键值对关键字表示索引。set中每个元素只包含一个关键字,set支持高效的关键字查询。
关联容器根据三个特性可以分为8种:1、set还是map;2、关键字是否可以重复,允许重复的容器名字中都包含单词“multi”;3、元素是否有序存储,无序存储的容器名字中都包含单词“unordered”。
类型map和multimap定义在头文件map中;set和muitlset定义在头文件set中;无序容器则定义在unordered_map或unordered_set中。
关联容器不支持顺序容器的位置相关的操作,例如push_back等,原因是关联容器中的元素是根据关键字存储的,这些操作对关联容器就失去了意义。
二、使用
1、使用map
定义map时需指定关键字和值的类型,在访问的时候通过first和second来访问元素的值,例子:

//统计每个单词在输入中出现的次数
map<string,size_t> word_count;
string word;
while(cin >> word){
    if(cin.get() == '\n'){
        break;
    } 
    ++word_count[word];
}
for(const auto &w : word_count){    //对map中的每个元素
    //打印
    cout<<w.first<<"出现了"<<w.second<<"次"<<endl;
}

2、使用set
定义set也需要指定关键字的类型,例子:

map<string,size_t> word_count;
set<sring> exclude = {"The","An","A","the","an","a"};
string word;
while(cin >> word){
    if(cin.get() == '\n'){
        break;
    } 
    //只统计不在set中的单词
    if(exclude.find(word) == exclude.end()){
        ++word_count[word];
    }
}
for(const auto &w : word_count){    //对map中的每个元素
    //打印
    cout<<w.first<<"出现了"<<w.second<<"次"<<endl;
}

3、multimap和multiset
一个map或set中的关键字必须是唯一的,容器multimap和multiset没有这个限制。

vector<int> v;
for(vector<int>::size_type i = 0;i != 10;i++){
    v.push_back(i);          
    v.push_back(i);             //保存两遍
}

set<int> iset(v.cbegin(),v.cend());
multiset<int> imset(v.cbegin(),v.cend());
cout<<v.size()<<endl;
cout<<iset.size()<<endl;            //不允许重复,打印10
cout<<imset.size()<<endl;           //允许重复,打印20

三、操作
关联容器额外的类型别名有三种:
key_type、mapped_type、value_type;

set<string>::value_type v1;         //v1是string类型
set<string>::key_type v2;           //v2是string类型,与value_type相同
map<string,int>::value_type m1;     //m1是pair<const string,int>
map<string,int>::key_type m2;       //m2是string
map<string,int>::mapped_type m3;    //m3是int,只有map才有这个定义

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

//*map_it是指向word_count第一个元素的迭代器
auto map_it = word_count.begin();
cout<<map_it->first<<endl;              //这里的类型是const的,不能改变
cout<<map_it->second<<endl;             //可以通过迭代器改变->second的值

//set的迭代器也是const的
set<int>::iterator set_it = iset.begin();

2.添加元素
关联容器的insert成员想容器中添加一个元素或一个元素范围。由于map和set包含不重复的关键字,所以插入一个已经存在的元素对容器没有任何影响。

#include <iostream>
#include <map>
#include <set>
#include <vector>
using namespace std;

int main(){
    vector<int> v = {1,2,3,4,4,3,2,1};
    set<int> s;
    s.insert(v.cbegin(),v.cend());
    cout<<s.size()<<endl;             //4

    s.insert({5,6,7,8});
    cout<<s.size()<<endl;             //8

    return 0;
} 

向map中添加元素的时候,需记住元素类型是pair。

//添加的四种方法
    map<string,int> m;
    m.insert({"joe",18});
    m.insert(make_pair("alex",19));
    m.insert(pair<string,int>("siri",20));
    m.insert(map<string,int>::value_type("james",22));

map调用insert方法后返回值是一个pair,告诉我们是否插入成功,这个pair的first成员是map的迭代器,指向具有指定关键字的元素(还是个pair);second成员是一个bool值,如果插入时关键字已经在容器中,insert什么事也不做,并且second的bool值为false,如果关键字不存在,插入后返回second中bool的值为true,表示插入成功。
向multiset或multimap添加元素方法类似,但是返回值不需要返回bool值,只需要返回指向新元素的迭代器。

3.删除元素
关联容器定义了三个版本的erase,与顺序容器一样可以传递给erase一个迭代器或是一个迭代器对来删除一个元素或者删除一个元素范围,指定元素被删除,返回值为void。
额外的一个erase操作是接受一个key_type参数,删除所有匹配给定关键字的元素,返回实际删除的元素的数量

4.map的下标操作
map和unordered_map提供了下标运算符和一个对应的at函数。set不支持下标,因为没有与关键字相对应的值。不能对multimap或一个unordered_multimap进行下标操作,因为一个关键字可能绑定多个值。
与其他下标运算符不同的是,如果关键字并不在map中,会为他创建一个元素插入到map中,并进行值初始化:

map<string,size_t> m;
m["anna"] = 1;              
//搜索关键值为anna的元素,未找到;
//将新键值对插入m中,关键字类型是const string,保存"anna",值进行初始化;
//提取新插入的元素,将1赋值给它

5.访问元素
主要讲的是两个函数,find()和count(),如果只是关心元素是否在容器中,那么find是最好的选择。但对于可以重复关键字的容器,count还会统计有多少个关键字重复的元素,当不需要计数的时候,最好还是使用find()。

set<int> s = {1,2,3,4,5,6,7};
s.find(1);          //返回一个迭代器,指向第一个key = 1 的元素
s.find(11);         //返回一个迭代器,指向s.end()
s.count(1);         //返回1
s.count(11);        //返回0
在multiset和multimap中查找的时候有一个特性很重要,那就是如果存在相同关键字的元素,这些元素会相邻存储。因此find和count同时使用可能会到达效果:
//查询一个作者写的书,authors是定义好的multimap
string name("Ernest Miller Hemingway");     //要查找的作者
auto num = authors.count(name);             //书的数量
auto it = authors.find(name);               //作者的第一本书
//用循环查找作者所有的书
while(num){
    cout<<it->second<<endl;
    ++it;
    --num;
}
还可以通过lower_bound和upper_bound来解决此问题:
for(auto l = authors.lower_bound(name),             //指向第一个
    auto u = authors.upper_bound(name);             //指向最后一个
    l != u;
    l++){

    cout<<l->second<<endl;
}

还有一种最直接的方式来处理这种情况,直接调用equal_range即可。此函数接受一个关键字做参数,返回一个迭代器pair。若关键字存在,pair中的第一个迭代器指向第一个与关键字相匹配的元素,第二个迭代器指向最后一个匹配元素之后的位置。若未找到匹配元素,则两个迭代器都指向关键字可以插入的位置。

for(auto pos = authors.equal_range(name);
    pos.first != pos.second;
    ++pos.first){
    cout<<pos.first->second<<endl;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值