1、关联容器额外的类型别名
除了容器通用的类型别名,如iterator,size_type等
关联容器有额外的类型别名:
- key_type:此容器的关键字类型
- mapped_type:关键字关联的类型,只适用于map
- value_type:对于set,与key_type相同,因为set只有关键字;对于map,是pair<const key_type,mapped_type
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
这里也可以看出来pair是map的元素。
2、关联容器迭代器
(1)、map
//关联容器的迭代器
map<string,int> word_count={{"hello",1},{"world",2}};
auto map_it = word_count.begin();
//*map_it是一个pair<const string,int>的引用
cout<<map_it->first<<" "<<map_it->second<<endl;
map_it->first="new";//错误,first是const的
++map_it->second;//正确,second是int
所有再一次理解map的value_type是pair<const key_type,mapped_type>。关键字,即first是不能修改的;而关联的值是可以修改的。
(2)、set
set的迭代器是const的,虽然set类型同时定义了iterator和const_iterator类型,但是都只允许只读访问set元素。于map一样,set的关键字是不能修改的。
所有总的来说,一个set迭代器只能都去元素的值,不能修改
而且map和set都支持begin和end,不过不同的是程序输出是按字典序输出的,因为元素是按字典序升序排列的。
3、关联容器的添加元素操作
容器的insert成员
可以向容器添加一个元素或者一个元素范围。
由于map和set(包括无序类型)包含的是不重复的关键字,所以插入一个已经存在的元素没有任何影响。
//1、insert
vector<int> ivec = {2,4,6,8,2,4,6,8};
set<int> set2;
set2.insert(ivec.cbegin(),ivec.cend());//接受一对迭代器,将迭代器范围内的元素拷贝到set中
set2.insert({1,3,5,7,1,3,5,7});//接受一个初始化列表,将列表中的元素拷贝到set中
同时也要注意map的元素是pair类型。
word_count.insert({"new",1});//插入一个pair对象
word_count.insert(make_pair("new",1));//通过make_pair生成一个pair对象
word_count.insert(pair<string,int>("new",1));//通过pair的构造函数生成一个pair对象
word_count.insert(map<string,int>::value_type("new",1));//通过value_type生成一个pair对象
关联容器的insert操作
- c.insert(v)或c.emplace(args) :添加单一元素;返回一个pair<iterator,bool>,返回一个pair,一个是指向指定关键字的迭代器,另一个是表示是否插入的bool值。
- c.insert(b,e)或c.insert(li):添加一个迭代器范围,或是初始化列表。返回void
- c.insert(p,v)或c.emplace(p,args):类似第一个,不过有一个迭代器p,用来指出从哪里开始搜索新元素应该存储的位置。返回一个指向具有指定关键字的元素
//重写单词计数程序
map<string,size_t> word_count1;
string word;
while(cin>>word){
auto ret = word_count1.insert({word,1});
if(!ret.second){
++ret.first->second;
}
}
这里ret返回是一个pair,ret.first是指向元素的迭代器,ret.second是表面是否插入新元素的bool值。
注:虽然map和set不能重复存储相同的关键字,但是multimap和multiset是可以重复的,比如一个作者,不可能只有一本书,所以就用到multimap。
multimap<string,string> authors;
authors.insert({"Barth,John","Sot-Weed Factor"});
//添加第二个元素,关键字已经存在
authors.insert({"Barth,John","Lost in the Funhouse"})
4、删除元素
删除元素的erase操作
- c.erase(k):从c中删除每个关键字为k的元素。返回一个size_type值,指出删除的元素的数量。
- c.erase(p):从c删除迭代器p指定的的元素。p不能是c.end(),必须是一个真实元素。返回指向一个删除元素之后元素的的迭代器。
- c.erase(b,e):删除迭代器范围中的元素,返回e=下一个元素的迭代器。
5、map的下标操作
首先,map和unordered_map容器提供了下标运算符和at函数。set和multi~的都没有
类似其他下标操作,map下标接受一个关键字:word_count["Anna"] = 1;
但是与其他下标不同的是,如果map中没有这个关键字,map会为他创建一个插入到map,关联值进行值初始化。所以我们只对非const的map进行下标操作。
- c[k]:返回关键字为k的关联值(mapped_type);如果不存在,创建一个插入到map
- c.at(k):访问关键字为k的元素,带参数检查;若k不存在,会抛出out_of_range的异常
6、访问元素
在一个关联容器中查找元素的操作
- 下标和at:只适用于非const的map和unoreded_map;
- c.find(k):返回一个迭代器,指向第一个关键字为k的元素,若k不在容器中,返回尾后迭代器
- c.count(k):返回关键字为k的元素的数量。对于不重复关键字的容器,只会是0和1;
- c.lower_bound(k):返回一个迭代器,指向第一个关键字不小于k的元素,也就是第一个具有k的元素;
- c.upper_bound(k):返回一个迭代器,指向第一个关键字大于k的元素;4和5不适用无序容器
- c.equal_range(k):返回一个迭代器pair,表示关键字等于k的元素的范围;如果不存在,pair的两个成员都是尾后迭代器
上面可以用find来代替map中的下标操作,因为下标会创建不存在map中的元素,在一些情况下下标就不合适,比如我们就想知道一个元素在不在map里面的情况,我们需要使用find。
在multimap和multiset中查找
1、find会返回第一个指定关键字的元素的迭代器。
string search_item("Alain de Botton");
auto entries = authors.count(search_item);//统计multimap中有多少个元素等于search_item
auto iter = authors.find(search_item);
//返回一个迭代器,指向第一个元素等于search_item的位置
while(entries){
cout<<iter->second<<endl;
++iter;
--entries;
}
2、lower_bound和upper_bound
另外,除了上面find和count,还可以用lower_bound和upper_bound两个操作
string search_item("Alain de Botton");
for(auto beg = authors.lower_bound(search_item);
iter!=authors.upper_bound(search_item);){
cout<<beg->second<<endl;
}
lower_bound返回第一个符合k的元素的迭代器,upper_bound返回最后一个之后的元素迭代器,如果没有元素匹配,两个返回一样的迭代器,指向关键字应该插入的位置
3、equal_range()
第三个方法是直接调用equal_range(),返回一个迭代器pair,表示关键字等于k的元素的范围;如果不存在,pair的两个成员都是尾后迭代器。下面再修改一次:
string search_item("Alain de Botton");
for(auto pos =authors.equal_range(search_item);pos.first!=pos.second;++pos.first){
cout<<pos.first->second<<endl;
}