1.关联容器
关联容器中的元素是按关键字来保存和访问的,反映了关键字的作用。
关联容器支持高效的关键字查找和访问,主要关联容器类型是map和set。
map中的元素是一些关键字-值,关键字起到索引的作用,值表示与索引相关联的数据。
map<key,value>
set中的每个元素只包含一个关键字,支持高效的关键字查询操作。
set<key>
类型map和multimap定义在头文件map中,set和multiset定义在头文件set中,无序容器则定义在头文件unordered_map和unordered_set中。
| map | 关联数组,保存关键字_值对 |
| set | 关键字即是值,只保存关键字的容器 |
| multimap | 关键字可重复出现的map |
| multiset | 关键字可重复出现的set |
| unordered_map | 用哈希函数组织的map |
| unordered_set | 用哈希函数组织的set |
| unordered_multimap | 关键字可重复的哈希map |
| unordered_multiset | 关键字可重复的哈希set |
2.使用关联容器
set<string> exclude = {"but","or","end","and"};
map类型通常被称为关联数组,与数组不同之处在于map的下标不一定是整数。
set是关键字的简单集合,用于查找某个值是否存在。
map<string,size_t> words;
类似于顺序容器,关联容器也是模板,定义map时必须指定关键字和值的类型。
从map中提取一个元素时,会得到一个pair类型的对象。
pair是一个模板类型,保存两个名为first和second的数据成员。
first成员保存关键字,second成员保存对应的值。
set容器起到类似于黑名单、白名单的作用。
与顺序容器相似,我们可以对一个关联容器的元素进行列表初始化。
set<string> exclude = {"the","end","and"};
3.关联容器概述
由于关联容器中元素是根据关键字存储的,因此关联容器不支持顺序容器的位置相关的操作。
每个关联容器都定义了一个默认构造函数,它创建一个指定类型的空容器。
map<string,size_t> words; //空容器
使用列表初始化空容器map,要将每个关键字_值对包围在花括号中。
map<string,string> authors = {{"lisi","lizhenyuan"},{"zhangsan","zhangsan2"}};
一个map或set中的关键字必须是唯一的。
容器multimap和multiset允许多个元素具有相同的关键字。
set容器会自动忽略具有相同的关键字的元素,不会报错。
3.1、关键字类型的要求
对于有序容器来说,关键字类型必须定义元素比较的方法。
默认情况下,标准库使用关键字类型的<运算符来比较两个关键字。
我们可以提供自己定义的比较函数来替代关键字上的<运算符,但该函数必须是行为正常的。
用来组织一个容器中元素的操作的类型也是该容器类型的一部分。
自定义的操作类型必须在类型列表中的元素类型后明确指出。
multiset<Sales_data,decltype(compare)*> bookstore;
//compare是比较函数,且使用decltype推导函数类型,加*表示函数指针
3.2、pair类型
标准库类型pair定义在头文件utility中。
pair是一个用来生成特定类型的模板,它的默认构造函数对数据成员进行值初始化。
pair<string,string> author{"james","joyce"}; //为每个成员提供初始化器
pair的数据成员是public的,分别命名为first和second。
| pair<T1,T2> p(v1,v2) pair<T1,T2> p = {v1,v2} | p是成员类型为T1和T2的pair 使用v1和v2初始化first、second |
| make_pair(v1,v2) | 返回一个用v1和v2初始化的pair pair的类型从v1、v2的类型推断出来 |
| p.first p.second | 返回p的名为first或second的数据成员 |
| p1 == p2 p1 != p2 | 当first和second的成员分别相等时,两个pair相等 |
在新标准下,我们可以对返回值进行列表初始化。
pair<string,int> process(vector<string> &v)
{
return {v.back(), v.back().size()}; //列表初始化pair
}
4.关联容器操作
| key_type | 容器类型的关键字类型 |
| mapped_type | 值的类型,只适用于map |
| value_type | 对于set,与key_type相同 对于map,为pair<const key_type,mapped_type> |
由于我们不能改变一个元素的关键字,因此关键字部分为const的。
使用作用域运算符来提取一个类型的成员。
map<string,int>::key_type v1; //v1是一个string
map<string,int>::mapped_type v2; //v2是一个int
map<string,int>::value_type v3; //v3是一个pair<const string, int>
4.1、关联容器迭代器
当解引用一个关联容器迭代器时,会得到一个类型为容器的value_type的值的引用。
auto map_it = words.begin();
//*map_it是指向一个pair<const string,size_t>对象的引用
set类型中的关键字是const的,该类型迭代器只允许访问元素,set类型迭代器是const的。
map和set类型都支持begin和end操作,迭代器按关键字升序来遍历元素。
由于关键字是const不能修改或重排容器元素,我们通常不对关联容器使用泛型算法。
4.2、添加元素
由于map和set包含不重复的关键字,因此插入一个已存在的元素对容器没有任何影响。
对一个map进行insert操作时,元素类型为pair。
//向words插入元素的方法
words.insert({word,1});
words.insert(make_pair(word,1));
words.insert(pair<string,size_t>(word,1));
words.insert(map<string,size_t>::value_type(word,1));
| c.insert(v) c.emplace(args) | v是value_type类型的对象,args用来构造一个元素 只有当元素的关键字不在c中时才插入元素。(set和map) |
| c.insert(b,e) c.insert(il) | b和e是迭代器,表示一个value_type类型值的范围 il是这种值的花括号列表,函数返回void |
| c.insert(p,v) c.emplace(P,args) | 迭代器p作为一个坐标,表示从哪里开始搜索新元素存储的位置 返回一个迭代器,指向具有给定关键字的元素 |
insert返回一个pair,该pair的first成员是迭代器,指向具有给定关键字的元素,second成员是bool值,表示插入是否成功。
4.3、删除元素
| c.erase(k) | 从c中删除每个关键字为k的元素,返回一个size_type值,指出删除的元素数量 |
| c.erase(p) | 从c中删除迭代器p指定的元素,p必须指向c中一个真实元素 返回一个指向p之后元素的迭代器 |
| c.erase(b,e) | 删除迭代器对b和e所表示的范围中的元素。返回e |
对于保存不重复关键字的容器,erase的返回值总是0或1。
4.4、map的下标操作
map和unordered_map容器提供了下标运算符和一个对应的at函数。
map下标运算符接受一个关键字,获得与此关键字相关联的值。
如果关键字并不在map中,运算符会为它创建一个元素并插入到map中。
| c[k] | 返回关键字为k的元素 如果k不在c中,添加一个关键字为k的元素,对其进行值初始化 |
| c.at(k) | 访问关键字为k的元素,带参数检查; 若k不在c中,抛出一个out_of_range异常 |
当对一个map进行下标操作时,会得到一个mapped_type对象,但解引用map迭代器时,会得到一个value_type对象。
4.5、访问元素
| c.find(k) | 返回一个迭代器,指向第一个关键字为k的元素 若k不在容器中,则返回尾后迭代器 |
| c.count(k) | 返回关键字等于k的元素的数量 对于不允许重复关键字的容器,返回值永远是0或1 |
| c.lower_bound(k) | 返回一个迭代器,指向第一个关键字不小于k的元素 |
| c.upper_bound(k) | 返回一个迭代器,指向第一个关键字大于k的元素 |
| c.equal_range(k) | 返回一个迭代器pair,表示关键字等于k的元素的范围 若k不存在,pair的两个成员均等于c.end() |
若我们只想知道一个给定关键字是否在map中,而不想改变map,可以使用find。
if(words.find("football") == words.end())
cout << "not in the map" << endl;
如果一个multimap或multiset中有多个元素具有给定关键字,则这些元素在容器中会相邻存储。
若我们想遍历具有相同关键字的所有值,最直观的方法是使用find和count。
auto counts = words.count(word);
auto iter = words.find(word);
while(counts)
{
cout << iter->second << endl;
++iter;
--counts;
}
还可以使用上下限函数来解决该问题。
for(auto beg = words.lower_bound(word),end = words.upper_bound(word);
beg != end; ++beg)
{
cout << beg->second << endl;
}
//若上下限函数返回相同的迭代器,则给定关键字不在容器中。
通过equal_range函数解决该问题:
for(auto pos = words.eual_range(word);pos.first != pos.second; ++pos.first)
{
cout << pos.first->second << endl;
}
//pos是pair,成员都是保存迭代器,表示与关键字匹配的范围
4.6、一个单词转换的map
std::map<string, string> buildmap(std::ifstream& map_file) //将转换规则从文件写入map中
{
std::map<string, string> trans_map; //创建map
string key; //临时保存key
string value; //替换的内容
while (map_file >> key && getline(map_file, value)) //读取第一个单词进入key中,剩余内容存入value
if (value.size() > 1) //检查是否有转换规则
trans_map[key] = value.substr(1); //跳过key与规则之间的空格,并存入value
else
throw std::runtime_error("no rule for " + key); //若无规则,则抛出异常
return trans_map; //返回转换规则map
}
const string& transform(const string& s, const std::map<string, string>& m) //单词转换
{
auto map_it = m.find(s); //在转换规则map寻找s
if (map_it != m.cend())
return map_it->second; //存在则使用替换短语
else
return s;
}
void word_transform(std::ifstream& map_file, std::ifstream& input)
{
auto trans_map = buildmap(map_file); //建立并保存转换规则map
string text; //临时保存读取数据
while (getline(input, text)) //读取一行输出
{
std::istringstream stream(text); //string流,用于读取每个单词
string word; //临时保存读取单词数据
bool firstword = true; //用于控制是否打印空格
while (stream >> word)
{
if (firstword)
firstword = false; //修改状态
else
cout << " "; //打印空格
cout << transform(word, trans_map); //转换
}
cout << endl;
}
}
int main()
{
std::ifstream in("file.txt");
std::ifstream in2("input.txt");
word_transform(in, in2);
return 0;
}
5.无序容器
无序关联容器由哈希函数和关键字类型的==运算符来组织容器中的元素。
理论上哈希技术能获得更好的平均性能,使用无序容器通常会有更好的性能。
无序容器也有允许重复关键字的版本,通常可以用一个无序容器替换对应的有序容器。
无序容器在存储上组织为一组桶,每个桶保存零个或多个元素,每个关键字是一个桶。
无序容器的性能依赖于哈希函数的质量和桶的数量和大小。
理想情况下,每个特定的值映射到唯一的桶,但也允许将不同关键字的元素映射到相同的桶。
| c.bucket_count() | 正在使用的桶的数目 |
| c.max_bucket_count() | 容器能容纳的最多的桶的数量 |
| c.bucket_size(n) | 第n个桶有多少元素 |
| c.bucket(k) | 关键字为k的元素在哪个桶中 |
| local_iterator | 用来访问桶中元素的迭代器类型 |
| const_local_iterator | 桶迭代器的const版本 |
| c.begin(n),c.end(n) | 桶n的首元素迭代器和尾后迭代器 |
| c.cbegin() c.cend() | 返回const_local_iterator |
| c.load_factor() | 每个桶的平均元素数量,返回float值 |
| c.max_load_factor() | c试图维护的平均桶大小 在需要时添加新桶,使得load_factor <= max_load |
| c.rehash(n) | 重组存储,使得桶的数目>= n |
| c.reserve(n) | 重组存储,但c可以保存n个元素且不必rehash |
默认情况下,无序容器使用关键字类型的==运算符来比较元素,hash<key_type>类型对象来生成每个元素的哈希值。
由于自定义类类型无hash模板和==运算符,因此不能直接定义关键字类型为自定义类类型的无序容器。
可以提供函数来代替==运算符和哈希值计算函数。
size_t hasher(const Sales_data &sd)
{
return hash<string()(sd.isbn());
}
bool qual(const Sales_data &left,const Sales_data& right)
{
return left.isbn() == right.isbn();
}

被折叠的 条评论
为什么被折叠?



