关联容器
- 1.关联容器与顺序容器的区分
- 2.关联容器的数据
- 2.1 数据存储
- 2.1.1 pair类型
- 2.1.2 关联容器的类型别名
- 2.1.3 关联容器迭代器
- 2.2 关联容器的初始化
- 3. 关联容器的增删改查
- 3.1 关联容器增加元素
- 3.2 删除关联容器的元素
- 3.3 修改map的元素
- 3.3 查找关联容器的元素
- 4. 关联容器使用示例
1.关联容器与顺序容器的区分
顺序容器,顾名思义就是其存储时是按照“位置顺序”来进行存储的,这里的顺序的含义就是我们常常理解的编号。以我们常用的vector
和string
为例,其每个元素都是按照编号来进行存储的,而且我们在顺序容器上的操作一般也是基于这些位置(编号)的遍历来实现的。与顺序容器不同的是,关联容器的设计原因就是为了实现“高效按值访问”,所以关联容器的数据存储核心一般都是“关键字”。关联二字的应有之义就是关键字和值建立关联关系。
另外,对于顺序容器而言,其底层存储结构是数组或者链表这种线性数据结构,而关联容器一般是由红黑树、哈希表等来实现的。所以顺序容器一般是基于位置处理,关联容器是基于关键字进行处理。
我们不能简单的将顺序顺序和关联容器的区分理解为是否有序;关联容器并不代表就是无序的,基于红黑树实现的关联容器都是有序的,只有基于哈希表实现的unordered_map和unordered_set
才是无序的。
2.关联容器的数据
2.1 数据存储
2.1.1 pair类型
关联容器的数据存储一般是采用键值对的形式。也就是我们常常见到的key-value
;我们在第一部分提到过,关联容器的访问是根据key值进行访问的,关联二字的含义也是key与value建立的关联关系。所以很有必要以一种数据类型来表达这个键值对。C++中提供了标准库类型pair
来表示一对数据,它定义于头文件utility
中。对于一个pair
数据来说,有以下几种方式进行定义。
pair<string,string> anon;
pair<string,int> p("hello",1);
pair<int,string> p1 = {1,"world"};
make_pair("hello","world");
我们可以使用first
和second
来访问其键和值两个数据成员。
cout<<p.first;
cout<<p.second;
2.1.2 关联容器的类型别名
在使用关联容器时,除了可以使用根前面的顺序容器相同的一些类型别名之外(如iterator,size_type,value_type,reference
),还有一些自己特殊的类型别名总结如下:
表1 关联容器特有的类型别名
名称 | 含义 |
---|
key_type | 容器的关键字类型 |
mapped_value | 容器的关键字的关联类型 |
value_type | 对于set,是于key_type相同;对于map,是一个pair类型,<key_type,mapped_type> |
2.1.3 关联容器迭代器
关联容器的迭代器使用与顺序容器的迭代器使用是相同的。但是对于解引用之后的操作要看其是否是一个pair
类型,如果是map
的话,对迭代器解引用就是一个pair
,需要使用first
和second
来进行键值访问。
对于set和map而言,如果用迭代器进行访问的话,是不能通过迭代器来进行修改key
值的。我们可以理解为:对于set
,迭代器必然是const_iterator
;对于map
,虽然迭代器可能为简单的iterator
,但是还是不能通过迭代器修改关键字成员的值。
2.2 关联容器的初始化
不能说与顺序容器的十分相似,只能说初始化一模一样。
3. 关联容器的增删改查
对于增删改查来说,方法还是那么些方法。只是在使用时可能要根据set和map的特性注意一下。下面只介绍特殊性。
3.1 关联容器增加元素
基于关联容器的特性,肯定是不能使用push_back
和push_front
这种方法。但是,关联容器的添加元素还是一样可是使用普通的insert
方法和emplace
方法。使用方法类似。
vector<int> ivec = {2, 4, 6, 7, 2, 4, 6, 7};
map<string,size_t> wordCount;
set<int> s;
s.insert(ivec.cbegin(),ivec.cend());
wordCount.insert({"wow",1});
使用虽然相同,但是在进行数据插入时,我们要区分好关联容器是否允许元素重复。对于set
等不允许重复关键字的关联容器而言,如果插入一个已有的键,那么就会直接忽略这个操作。而对于multiset
等允许重复的关联容器而言,就会继续执行插入。
除了使用上面的函数进行元素添加,我们通常更习惯使用下标来添加元素,示例如下:
while(cin>>word){
++word_count[word];
}
3.2 删除关联容器的元素
同前面一样,由于使用的是根据关键字访问,所以pop_back、pop_back
方法是不能再使用的。我们使用erase方法来进行关联容器的数据删除。此时的erase特性如下
表2 关联容器的数据删除操作
方法形式 | 使用细节 |
---|
erase(k) | 删除容器中关键字为k的元素,返回值为size_t表示删除元素的数量 |
erase§ | 删除迭代器p指定的元素,其中p必须真实指向一个容器元素,返回p之后元素的迭代器 |
erase(p,e) | 删除[p,e)范围内的元素,返回e |
map<int,string> ma = {{1,"hello"},{2,"world"},
{3,"nihao"},{4,"shijie"}
};
cout<<"删除的元素个数为:"<<ma.erase(2)<<endl;
3.3 修改map的元素
关联容器的键不能修改,但是其值却是可以修改的,虽然关联容器某种程度上而言并不是根据位置序号访问,但它可以用特殊的下标——键
进行访问元素。使用形式与顺序容器一样,可以通过at
函数或者[]
来实现。
cout<<"ma[1] is :"<<ma[1]<<endl;
cout<<"ma[1] is :"<<ma.at(1)<<endl;
auto m =ma[1];
使用map的下标操作还可以进行元素的添加,如果使用一个不在容器中的关键字作为下标,就会添加一个具有此关键字的元素到map
中。这也就造成在顺序容器和关联容器使用下标访问时返回值是不一样的,前者是元素类型本身,后者则是一个对应的mapped_value
.
3.3 查找关联容器的元素
在关联容器中使用的查找元素的方法一般有以下两种
表3 关联容器的数据查找操作
方法形式 | 使用细节 |
---|
find(k) | 查找关键字为k的元素是否存在,若存在返回指向第一个关键字为k的元素的迭代器,否则返回尾后迭代器end |
count(k) | 返回容器中关键字为k的元素数量;注意若是不允许重复的容器,方法就类似于find函数。 |
4. 关联容器使用示例
下面的例子是基于C++primer第五版中的单词转换程序的实现。
#include<iostream>
#include<fstream>
#include<sstream>
#include<map>
#include<stdexcept>
using std::ifstream;using std::endl;
using std::cout;using std::cin;
using std::string;using std::map;
using std::runtime_error;
using std::istringstream;
map<string,string> buildMap(ifstream& mapfile){
map<string,string> w_map;
string key;
string value;
while(mapfile>>key && getline(mapfile,value)){
if(value.size()<1){
throw runtime_error("no similiar rule\n");
}
w_map.insert({key,value.substr(1)});
}
return w_map;
}
const string &transform(const string &s,const map<string,string> &m){
auto mp = m.find(s);
if(mp != m.end()){
return mp->second;
}
else return s;
}
void wordTransform(ifstream& mapFile,ifstream& input){
map<string,string> w_map = buildMap(mapFile);
string text;
while(getline(input,text)){
istringstream strin(text);
string word;
bool first = true;
while(strin>>word){
if(first){
first =false;
}else cout<<" ";
cout<<transform(word,w_map);
}
cout<<endl;
}
}
int main(){
ifstream mapfile("./STL_associative/rule.txt");
if(!mapfile){
throw runtime_error("no rules file");
}
ifstream input("./STL_associative/tran.txt");
if(!input){
throw runtime_error("no trans file");
}
wordTransform(mapfile,input);
return 0;
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)