C++ Primer 5th学习笔记10 关联容器

关联容器

1 使用关联容器

  关联容器支持高效的关键字查找和访问,两个主要的关联容器类型是mapsetmap中的元素是关键字-值(key-value)对:关键字起到索引作用,值则表示与索引相关联的数据。set中每个元素只包含一个关键字,set支持高效的关键字查询操作——检查一个给定的关键字是否在set
  标准库提供8个关联容器,如下表格所示。类型mapmultimap定义在头文件map中;类型setmultiset定义在头文件set中;无序容器则定义在头文件unordered_map和unordered_set中。

关联容器类型
按关键字有序保存元素
名称 说明
map 关联数组;保存关键字-值对
set 关键字即值,即只保存关键字的容器
multimap 关键字可重复出现的map
multiset 关键字可重复出现的set

无序集合

名称 说明
unordered_map 用哈希函数组织的map
unordered_set 用哈希函数组织的set
unordered_multimap 哈希组织的map;关键字可以重复出现
unordered_multiset 哈希组织的set;关键字可以重复出现

使用map
 一个经典的使用关联数组的例子,单词计数程序示例如下:

//统计每个单词在输入中出现的次数
map<string, size_t> word_count;        //string到size_t的空map
string word;
while (cin >> word)
        ++word_count[word];        //提取word的计数器并将其加1
for(const auto &w : word_count)    //对map中的每个元素打印结果
{
    count << w.first << " occurs " << w.second
          << ((w.second > 1) ? " times" : " time") << endl;
}

其中++word_count[word]是使用一个string作为下标,获得与此string相关联的size_t类型的计数器。如果word还未在map中,下标运算符会创建一个新元素,其关键字为word,值为0,然后不管元素是否是新创建,都将其值加1;
当从map中提取一个元素是,会得到一个pair类型的对象,pair是一个模板类型,保存两个名为first和second的(公有)数据成员。其中map使用的pair用first成员保存关键字,用second成员保存对应的值。

使用set
 如这样一个例子,使用set保存想忽略的单词,只对不在集合中的单词统计出现次数:

//统计输入中每个单词出现的次数
map<string, size_t> word_count;        //string到size_t的空map
set<string> exclude = {"The", "But", "And", "Or", "An", "A",
                       "the", "but", "and", "or", "an", "a"};
string word;
while (cin >> word)
{  
    //只统计不在exclude中的单词
    if (exclude.find(word) == exclude.end())
    {
        ++word_count[word];       //获取并递增word的计数器
    }
}

2 关联容器概述

2.1 关联容器概述

 当定义一个map时,必须指明关键字类型和指明值类型。定义一个set时,只需要指明关键字类型。multimap和multiset则是允许多个元素具有相同的关键字。
 标准库使用关键字类型的<运算符来比较关键字。也可以自定义操作来代替关键字上的<运算符,示例如下:

bool compareIsbn(const Sales_data &lhs, const Sales_data &rhs)
{
    return lhs.isbn < rhs.isbn();
}

//使用multiset容器
multiset<Sales_data, decltype(compareIsbn)*> bookstore(compareIsbn);

这里使用decltype来指出自定义操作的类型,当使用decltype来获得一个函数指针类型时,必须加上一个*来指出要使用一个给定函数类型的指针。用compareIsbn来初始化bookstore对象,这表示向bookstore添加元素时,通过调用compareIsbn来为这些元素排序。

2.2 pair类型

 pair类型定义在头文件utility中。一个pair保存两个数据成员,两个成员均是public的,其命名为first和second。

pair上的操作
操作 名称
pair<T1, T2> p; p是一个pair,成员类型为T1T2
pair<T1, T2> p(v1, v2); p是一个成员类型为T1T2的pair
pair<T1, T2> p = {v1, v2}; 等价于p(v1, v2)
make_pair(v1, v2) 返回一个用v1v2初始化的pair
p.first 返回p名为first的(公有)数据成员
p.second 返回p名为second的(公有)数据成员
p1 relop p2 关系运算符(<、<、<=、>=)按字典定义
p1 == p2 p1 != p2 判断两个pair是否相等,当first和second都相等时,两个pair才相等

3 关联容器操作

关联容器额外的类型别名
别名 描述
key_type 此类型的关键字类型
mapped_type 每个关键字关联的类型,只适用于map
value_type 对于set,与key_type相同;对于map,为pair <cosnt key_type, mapped_type>

tips:注意上述pair类型中的关键字为const类型

3.1 关联容器迭代器

遍历关联容器
 map和set容器都支持begin和end操作,使用迭代器来遍历容器,示例如下:

//获取一个指向首元素的迭代器
auto map_it = word_count.cbegin();
//比较当前迭代器和尾迭代器
while (map_it != word_count.cend)
{
    //解引用迭代器,打印关键字-值对
    cout << map_it->first << " occurs "
         << map_it->second << " times" << endl;
         ++map_it;    //递增迭代器,移动到下一个元素
}

3.2 添加元素

 由于map和set(以及对应的无序类型)包含不重复的关键字,因此插入一个已存在的元素对容器没有任何影响。但向map中进行insert操作时,必须记住元素类型是pair。

关联容器insert操作
操作 描述
c.inset(v) v是value_type类型的对象;args用来构造一个元素
c.emplace(agrs) 对于map和set,只有当元素关键字不存在c中时才构造元素,函数返回一个pair
c.insert(b,e) b和e迭代器,表示一个c::value_type类型值的范围;
c.insert(il) il是这种值的花括号列表
c.insert(p, v) 将迭代器p作为一个提示
c.emplace(p, args) 指出从哪里开始插入新元素应该储存的位置

检测insert的返回值
 对于不包含重复关键字的容器,添加单一元素的insert和emplace版本返回一个pair,pair的first成员是一个迭代器,指向具有给定关键字的元素;second成员是一个bool值,指出元素是插入成功,还是已存在于容器中,若已存在,则返回false。使用insert的示例如下:

map<string, size_t> word_count;        //从string到size_t的空map
string word;
while (cin >> word)
{
    //添加一个元素,关键字等于word,值为1
    //若word已在word_count中,insert什么也不做
    auto ret = word_count.insert({word, 1});
    if (!ret.second)        //word已在word_count中
        ++ret.first->second;    //递增计数器
}

展开递增语句
++ret.first->second; //递增计数器
ret 保存insert返回的值,是一个pair;
ret.first 是pair的第一个成员,是一个map迭代器,指向具有给定关键字的元素
ret.first-> 解引用此迭代器,提取map中的元素,元素也是一个pair
ret.first->second map中元素的值部分
++ret.first->second 递增此值

3.2 删除元素

从关联容器删除元素
操作 描述
c.erase(k) 从c中删除每个关键字为k的元素。返回一个size_type值,指出删除的元素的数量
c.erase(p) 从c中删除迭代器p指定的元素,元素必须存在,返回指向p之后元素的迭代器
c.erase(b, e) 删除迭代器对b和e所表示的范围中的元素,返回e

3.3 map的下标操作

 map下标运算符接受一个索引(即一个关键字),来获取与此关键字相关联的值。如果关键字不在map中,会为它创建一个元素插入到map中,关联值将进行值初始化。因此,只可以对非const的map使用下标操作。

map和unordered_map的下标操作
操作 描述
c[k] 返回关键字为k的元素,若不存在k,则创建k
c.at[k] 访问关键字为k的元素,带参数检查。若不存在k,则抛出out_of_range

对map进行下标操作时,会获得一个mapped_type对象;当解引用一个map迭代器时,会得到一个value_type对象

3.4 访问元素

在一个关联容器中查找元素的操作
操作 描述
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的元素的范围

Tips:lower_bound和upper_bound不适用与无序容器;下标和at操作只适用于非const的map和unordered_map

4 无序容器

  新标准定义了4个无序关联容器,这些容器使用一个哈希函数和关键字类型的==运算符

使用无序容器
  使用unordered_map重写最初的单词计数程序

//统计出现次数,但单词不会按字典序排列
unordered_map<string, size_t> word_count;
string word;
while(cin >> word)
        ++word_count[word];        //提取并递增word的计数器
for (const auto &w : word_count)   //对map中的每个元素打印结果
    cout << w.first << " occurs " << w.second
         << ((w.second > 1) ? " times" : " time") << endl;

管理桶
 无序容器在储存组织为一组桶,每个桶保存零个或多个元素。容器将具有一个特定哈希值的所有的元素都保存在相同的桶中。无序容器的性能依赖于哈希函数的质量和桶的数量和大小。

无序容器管理操作

桶接口

操作 描述
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(n), c.cend(n) 返回const_local_iterator

哈希策略

操作 描述
c.load_factor() 每个桶的平均元素数量,返回float值
c.max_load_factor() c试图维护的平均桶大小,返回float值
c.rehash(n) 重组储存,使得bucket_count >= n且 bucket_count >size/max_load_factor
c.reserve(n) 重组储存,使得c可以保存n个元素气人不必rehash

 可以直接定义关键字是内置类型(包括指针类型)、string还是智能指针类型的无序容器。也可以自定义hash比较函数,示例如下:

size_t hasher (const Sales_data &sd)
{
    return hash<string>() (sd.isbn());
}
bool eqOp (const Sales_data &lhs, const Sales_data &rhs)
{
    return lhs.isbn() == rhs.isbn();
}
//使用上述函数来定义一个unordered_multiset
using SD_multiset = unordered_multiset<Sales_data, 
                    decltype(hasher)*, decltype(eqOp)*>;
//参数是桶大小。哈希函数指针和相等性判断运算符指针
SD_multiset bookstore(42, hasher, eqOp);

猜你喜欢

转载自blog.csdn.net/qq_18150255/article/details/89032806