11.关联容器

news2024/11/27 12:11:58

文章目录

  • 关联容器
    • 11.1使用关联容器
      • 使用map
      • 使用set
    • 11.2关联容器概述
      • 11.2.1定义关联容器
        • 初始化multimap或multiset
      • 11.2.2关键字类型的要求
        • 有序容器的关键字类型
        • 使用关键字类型的比较函数
      • 11.2.3pair类型
        • 创建pair对象的函数
    • 11.3关联容器操作
      • 11.3.1关联容器迭代器
        • set的迭代器是const的
        • 遍历关联容器
        • 关联容器和算法
      • 11.3.2添加元素
        • 向set添加元素
        • 向map添加元素
        • 检测insert的返回值
        • 向multiset或multimap添加元素
      • 11.3.3删除元素
      • 11.3.4map的下标操作
      • 11.3.5访问元素
        • 在multimap或multiset中查找元素
        • 一种不同的,面向迭代器的解决办法
        • equal_range函数
    • 11.4无序容器
      • 管理桶
      • 无序容器对关键字类型的要求

关联容器

类型mapmultimap定义在头文件map中;setmultiset定义在头文件set中;无序容器则定义在头文件unordered_mapunordered_set中。

在这里插入图片描述

11.1使用关联容器

使用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中的每个元素
    // 打印结果
    cout << w.first << " occurs " << w.second
         << ((w.second > 1) ? " times" : " time") << endl;
}

当从map中提取一个元素时,会得到一个pair类型的对象。简单来说,pair是一个模板类型,保存两个名为firstsecond的(公有)数据成员。map所使用的pairfirst成员保存关键字,用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];
	}
}

11.2关联容器概述

关联容器都支持普通容器操作:

在这里插入图片描述

但是不支持顺序容器的位置相关的操作,例如push_frontpush_back。原因是关联容器中元素是根据关键字存储的,这些操作对关联容器没有意义。而且,关联容器也不支持构造函数或插入操作这些接受一个元素值和一个数量值的操作。
关联容器的迭代器都是双向的。

11.2.1定义关联容器

map<string, size_t> word_count;
set<string> exclude = {"The", "But", "And", "Or", "An", "A",
                       "the", "but", "and", "or", "an", "a"};
map<string, string> authors = {
        {"Joyce", "James"},
        {"Austen", "Jane"},
        {"Dickens", "Charles"}
};

初始化multimap或multiset

multimapmultiset都允许多个元素具有相同的关键字。

// 定义一个有20个元素的vector,保存0到9每个整数的两个拷贝。
vector<int> ivec;

for (vector<int>::size_type i = 0; i != 10; ++i) {
	ivec.push_back(i);
	ivec.push_back(i);	// 每个数重复保存一次
}

// iset包含来自ivec的不重复的元素;miset包含所有20个元素。
set<int> iset(ivec.cbegin(), ivec.cend());
multiset<int> miset(ivec.cbegin(), ivec.cend());

cout << ivec.size() << endl;	// 20
cout << iset.size() << endl;	// 10
cout << miset.size() <<endl;	// 20

11.2.2关键字类型的要求

对于有序容器,mapmultimapset以及multiset,关键字类型必须定义元素比较的方法。默认情况下,标准库使用关键字类型的<运算符来比较两个关键字。

有序容器的关键字类型

可以提供自己定义的操作来代替关键字上的<运算符。所提供的操作必须在关键字类型上定义一个严格弱序。可以将严格弱序看作小于等于。
在实际编程中,重要的是,如果一个类型定义了行为正常的<运算符,则它可以用作关键字类型。

使用关键字类型的比较函数

用来组织一个容器中元素的操作的类型也是该容器类型的一部分。为了指定使用自定义的操作,必须在定义关联容器类型时提供此操作的类型。
在尖括号中出现的每个类型,就仅仅是一个类型而已。当创建一个容器(对象)时,才会以构造函数参数的形式提供真正的比较操作(其类型必须与在尖括号中指定的类型相吻合)。

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

// decltype必须加上一个*来指出要使用一个给定函数类型的指针
multiset<Sales_data, decltype(compareIsbn) *> bookstore(compareIsbn);

11.2.3pair类型

定义在头文件utility中。pair的默认构造函数对数据成员进行值初始化。

// 包含两个空string
pair<string, string> anon;
// string为空,size_t为0。
pair<string, size_t> word_count;
// 保存一个空string和一个空vector
pair<string, vector<int>> line;
pair<string, string> author{"James", "Joyce"};

在这里插入图片描述

创建pair对象的函数

// 新标准。较早的c++版本中,不允许用花括号包围的初始化器来返回pair这种类型的对象,必须显式构造返回值。
// 还可以用make_pair来生成pair对象。
pair<string, int> process(vector<string> &v) {
	// 处理v
	if (!v.empty()) {
		// 旧标准
		// return pair<string, int>(v.back(), v.back().size());
		// make_pair
		// return make_pair(v.back(), v.back().size());
		return {v.back(), v.back().size()};	// 列表初始化
	} else {
		return pair<string, int>();	// 隐式构造返回值
	}
}

11.3关联容器操作

在这里插入图片描述

set<string>::value_type v1;	// v1是一个string
set<string>::key_type v2;	// v2是一个string
// 由于不能改变一个元素的关键字,因此pair的关键字部分是const的。
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

11.3.1关联容器迭代器

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

// 获得指向word_count中一个元素的迭代器
auto map_it = word_count.begin();
// *map_it是指向一个pair<const string, size_t>对象的引用
cout << map_it -> first;
cout << " " << map_it -> second;
map_it->first = "new key";	// 错误:关键字是const的
++map_it->second;

set的迭代器是const的

虽然set同时定义了iteratorconst_iterator类型,但两种类型都只允许访问set中的元素。一个set中的关键字也是const的。

遍历关联容器

当使用一个迭代器遍历一个mapmultimapsetmultiset时,迭代器按关键字升序遍历元素。

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

关联容器和算法

通常不对关联容器使用泛型算法。关键字是const的特性意味着不能将关联容器传递给修改或重排容器元素的算法,因为这类算法需要向元素写入值,而set类型中的元素是const的,map中的元素是pair,其第一个成员是const的。
关联容器可用于只读取元素的算法。但是,很多这类算法都要搜索序列。由于关联容器中的元素不能通过它们的关键字进行(快速)查找,因此对其使用泛型搜索算法几乎总是个坏主意。通常情况下,使用其find成员会好很多。
在实际编程中,如果真要对一个关联容器使用算法,要么是将它当作一个源序列,要么当作一个目的位置。例如,可以用泛型copy算法将元素从一个关联容器拷贝到另一个序列。类似的,可以调用inserter将一个插入器绑定到一个关联容器。通过使用inserter,可以将关联容器当作一个目的位置来调用另一个算法。

11.3.2添加元素

在这里插入图片描述

向set添加元素

vector<int> ivec = {2, 4, 6, 8, 2, 4, 6, 8};
set<int> set2;
set2.insert(ivec.cbegin(), ivec.cend());    // set2有4个元素
set2.insert({1, 3, 5, 7, 1, 3, 5, 7});  // set2现在有8个元素

向map添加元素

word_count.insert({word, 1});
word_count.insert(make_pair(word, 1));
word_count.insert(pair<string, size_t>(word, 1));
word_count.insert(map<string, size_t>::value_type(word, 1));

检测insert的返回值

// 统计每个单词在输入中出现次数的一种更烦琐的方法
map<string, size_t> word_count;	// 从string到size_t的空map
string word;

while (cin >> word) {
	// 插入一个元素,关键字等于word,值为1;
	// 若word已在word_count中,insert什么也不做。
	// 旧版本的编译器,ret的声明和初始化为:pair<map<string, size_t>::iterator, bool> ret = ...
	auto ret = word_count.insert({word, 1});

	if (!ret.second) {
		++ret.first->second;
	}
}

向multiset或multimap添加元素

一个multi容器中的关键字不必唯一,在这些类型上调用insert总会插入一个元素:

multimap<string, string> authors;
// 插入第一个元素,关键字为Barth, John
aothors.insert({"Barth, John", "Sot-Weed Factor"});
// 正确:添加第二个元素,关键字也是Barth, John
aothors.insert({"Barth, John", "Lost in the Funhouse"});

11.3.3删除元素

在这里插入图片描述

11.3.4map的下标操作

set不支持下标,因为set中没有与关键字相关联的值,元素本身就是关键字。不能对一个multimap或一个unorderd_multimap进行下标操作,因为这些容器中可能有多个值与一个关键字相关联。

在这里插入图片描述

由于下标运算符可能插入一个新元素,只可以对非constmap使用下标操作。
需要注意的是,通常情况下,解引用一个迭代器所返回的类型与下标运算符返回的类型是一样的。但对map进行下标操作时,会获得一个mapped_type对象;但当解引用一个map迭代器时,会得到一个value_type对象。

11.3.5访问元素

在这里插入图片描述

在multimap或multiset中查找元素

如果一个multimapmultiset中有多个元素具有给定关键字,则这些元素在容器中会相邻存储

string search_item("Alain de Botton");  // 要查找的作者
auto entries = authors.count(search_item);  // 元素的数量
auto iter = authors.find(search_item);  // 此作者的第一本书

while (entries) {
    cout << iter->second <<endl;
    ++iter;
    --entries;
}

一种不同的,面向迭代器的解决办法

lower_bound返回的迭代器指向第一个具有给定关键字的元素,而upper_bound返回的迭代器则指向最后一个匹配到的给定关键字的元素之后的位置。如果元素不存在,则lower_boundupper_bound会返回相等的迭代器,指向一个不影响排序的关键字插入位置。
如果查找的元素具有容器中最大的关键字,则此关键字的upper_bound返回尾后迭代器。如果关键字不存在,且大于容器中任何关键字,则lower_bound返回的也是尾后迭代器。

for (auto beg = authors.lower_bound(search_item),
             end = authors.upper_bound(search_item);
     beg != end; ++beg) {
    cout << beg->second << endl;
}

equal_range函数

equal_range返回一个迭代器pair。若关键字存在,则第一个迭代器指向第一个与关键字匹配的元素,第二个迭代器指向最后一个匹配元素之后的位置。若未找到匹配元素,则两个迭代器都指向关键字可以插入的位置。

for (auto pos = authors.equal_range(search_item);
     pos.first != pos.second; ++pos.first) {
    cout << pos.first->second << endl;
}

11.4无序容器

无序容器使用一个哈希函数和关键字类型的==运算符。

管理桶

无序容器在存储上组织为一组桶,每个桶保存零个或多个元素。无序容器使用一个哈希函数将元素映射到桶。为了访问一个元素,容器首先计算元素的哈希值,它指出应该搜索哪个桶。容器将具有一个特定哈希值的所有元素都保存在相同的桶中。如果容器允许重复关键字,所有具有相同关键字的元素也都会在同一个桶中。因此,无序容器的性能依赖于哈希函数的质量和桶的数量和大小。
对于相同的参数,哈希函数必须总是产生相同的结果。理想情况下,哈希函数还能将每个特定的值映射到唯一的桶。但是,将不同关键字的元素映射到相同的桶也是允许的。当一个桶保存多个元素时,需要顺序搜索这些元素来查找想要的那个。计算一个元素的哈希值和在桶中搜索通常都是很快的操作。但是,如果一个桶中保存了很多元素,那么查找一个特定元素就需要大量的比较操作。

在这里插入图片描述

无序容器对关键字类型的要求

默认情况下,无序容器使用关键字类型的==运算符来比较元素,它们还使用一个hash<key_type>类型的对象来生成每个元素的哈希值。标准库为内置类型(包括指针)提供了hash模板。还为一些标准库类型,包括string和智能指针类型定义了hash。因此,可以直接定义关键字是这些类型的无序容器。
但是,不能直接定义关键字类型为自定义类类型的无序容器,必须提供自己的hash模板版本。
不使用默认的hash,而是使用另一种方法,类似于为有序容器重载关键字类型的默认比较操作。

// 为了能将Sales_data用作关键字,需要提供函数来替代==运算符和哈希值计算函数。
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 = unorderd_multiset<Sales_data, decltype(hasher) *, decltype(eqOp) *>;
// 参数是桶大小、哈希函数指针和相等性判断运算符指针
SD_multiset bookstore(42, hasher, eqOp);

// 如果定义了==运算符,则可以只重载哈希函数:
unordered_set<Foo, decltype(FooHash) *> fooSet(10, FooHash);

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/89851.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

为什么Docker比VM虚拟机快?

(1)docker有着比虚拟机更少的抽象层 由于docker不需要Hypervisor(虚拟机)实现硬件资源虚拟化&#xff0c;运行在docker容器上的程序直接使用的都是实际物理机的硬件资源。因此在CPU、内存利用率上docker将会在效率上有明显优势。 (2)docker利用的是宿主机的内核,而不需要加载操…

基于springboot招生管理系统设计与实现的源码+文档

摘 要 在Internet高速发展的今天&#xff0c;我们生活的各个领域都涉及到计算机的应用&#xff0c;其中包括招生管理系统的网络应用&#xff0c;在外国招生管理系统已经是很普遍的方式&#xff0c;不过国内的管理网站可能还处于起步阶段。招生管理系统具有招生公告信息管理功能…

实训任务一

文章目录实训任务一一、实训任务1、创建并且配置三个虚拟机2、创建SSH连接3、实现IP地址与主机名的映射4、关闭和禁用防火墙5、创建目录结构6、压缩打包7、安装软件包8、创建脚本文件9、直接运行脚本10、虚拟机相互免密登录11、远程拷贝文件实训任务一 需求&#xff1a;熟练掌…

java+MySQL 基于ssm高校创新实践学分认定系统

随着现代实践学分认定的快速发展,可以说实践学分认定已经逐渐成为现代实践学分认定过程中最为重要的部分之一。但是一直以来我国传统的实践学分认定并没有建立一套完善的行之有效的实践学分认定系统,传统的实践学分认定已经无法适应高速发展,无论是从效率还是从效果来看都远远的…

问题解决之:chatGPT 登录页面的 google 验证 reCAPTCHA 弹不出来

文章目录问题描述自己的境况分析结论问题描述 今天我到了图书馆想访问一下 chatgpt&#xff0c;挂了 vpn 之后所有的浏览器都无法弹出 reCAPTCHA 人机验证&#xff0c;即使我更换了不同的 vpn 和为 chrome 的 reCAPTCHA 设置了重定向也无法成功 正常情况&#xff1a;应该弹…

基于B\S的《C语言程序设计》学习网站的设计与实现

开发工具(eclipse/idea/vscode等)&#xff1a; 数据库(sqlite/mysql/sqlserver等)&#xff1a; 功能模块(请用文字描述&#xff0c;至少200字)&#xff1a; 1)系统平面设计&#xff1a;设计精美、简洁且清爽的系统界面。 2)课程信息管理&#xff1a;对课程的基本信息、课程目标…

Mybatis源码分析(二)Mybatis-config.xml的初始化

目录一 环境搭建二 配置文件初始化2.1 ClassLoader2.2 获取配置文件官网&#xff1a;mybatis – MyBatis 3 | 简介 参考书籍&#xff1a;《通用源码阅读指导书&#xff1a;MyBatis源码详解》 易哥 参考文章&#xff1a; 一看你就懂&#xff0c;超详细java中的ClassLoader详解A…

【Unity3DRPG入门学习笔记第三卷】PolyBrush 构建场景

一、安装 Polybrush 导入样例 我新建了一个新文件夹 Plugins 用来管理 打开 Polybrush Window 二、使用 Polybrush 1. 选中物体&#xff0c;使用第一个工具&#xff0c;会发现可以显示顶点&#xff0c;可以改变网格&#xff0c;例如我们可以上下拖拽地面改变地形 正常左键点…

Java 包装类

Java包装类\huge{Java \space 包装类}Java 包装类 概述 所谓的包装类&#xff0c;通俗来讲其实就是888种基本数据类型对应的引用类型&#xff08;本质就是引用类型&#xff09;。 ❗❗❗尤其注意charcharchar对应的包装类的名称是charactercharactercharacter&#xff0c;in…

大数据学习:shell基础

文章目录一、常用shell命令任务一&#xff1a;查看/etc目录信息前5行信息任务二&#xff1a;查看/etc/profile文件后5行信息二、grep命令选项参数任务一&#xff1a;抓取/etc目录下的python信息任务二&#xff1a;抓取/etc/profile文件里的dev信息任务三&#xff1a;抓取用户数…

Revit运行很卡?这些招数你学会(废)了吗?

在日常的项目实施过程中&#xff0c;我们经常会感觉到Revit运行越来越慢。当然&#xff0c;和我们经常吐槽的软件本身有一定的关系&#xff0c;除此之外&#xff0c;根据我这些年的经验总结&#xff0c;规避掉以下问题可大幅度缓解Revit卡顿的问题。 01禁用结构分析选项 我们…

一条道简单的算法引发的思考

前言 新一季的 Rick&Morty 已经上线&#xff0c;剧集质量虽然有所下降&#xff0c;但 E03 中的 SheepCounter 挺有意思。自己照着剧中的设定开发了一款界面极其相似、交互更为丰富的小程序&#xff0c;小程序的终极目标只有一个&#xff1a;数羊&#xff01;数羊&#xff…

大数据Kudu(六):Kudu Java Api操作

文章目录 ​​​​​​Kudu Java Api操作 一、​​​​​​​​​​​​​​添加Maven依赖

zos-open gb28181,rtsp,rtmp,hls直播储存回放,上下级级联

fslib框架 fslib框架是一套可运行于生产环境的支持c/c线程死锁,线程cpu资源统计,死机时自动记录死机所对应的源码位置的调试框架,部分功能支持php语言&#xff1b;fslib框架内置了很多实用库配置库(FsConfig)--支持向上向下兼容的配置模块&#xff0c;同时可以导出与导入json和…

Ajax(三)

1.form表单的基本使用 1.1 什么是表单 表单在网页中主要负责数据采集功能。HTML中的<form>标签&#xff0c;就是用于采集用户输入的信息&#xff0c;并通过<form>标签的提交操作&#xff0c;把采集到的信息提交到服务器端进行处理。 1.2 表单的组成部分 表单标签…

java+MySQL 基于ssm的网上定点餐外卖系统

网上订餐不是一蹴而就的事情,它需要的是线上线下的共同努力。对于线上来说,安全、稳定、功能完善的网站构建必不可少,这是主要的也是最重要的一部分,网站是“脸面”,好的脸面会吸引更多的顾客光顾。而对于线下来说,好的菜品是一个订餐网站的支柱,我们不能仅靠各色各样的图片满足…

mockito的详细使用

目录 1.概述 2.使用 2.1.依赖 2.2.校验 2.2.1.值校验 2.2.2.顺序校验 2.2.3.指定返回 2.3.注解 2.3.1.Mock 2.3.2.Spy 2.3.3.Captor 2.3.4.InjectMocks 1.概述 mock&#xff0c;一种JAVA单元测试技术&#xff0c;mock允许使用模拟对象替换测试中的系统部件&#xf…

【Redis】Redis 分布式锁

文章目录概述Redis 实现分布式锁加锁释放锁死锁概述 在单体项目中&#xff0c;我们处理多线程同时操作某一处代码块或者变量时就使用 Synchronized 或者 Lock 锁去保证数据的安全性&#xff0c;但是&#xff0c;现在我们基本上都是使用微服务&#xff0c;当我们把服务部署到多…

一文说透小程序插件及其作用价值

最近工作接触小程序插件比较多&#xff0c;就想着不如跟大家系统分享一下小程序插件相关的内容。 首先&#xff0c;我们要先弄清楚小程序插件究竟是什么&#xff1f; 简单来说&#xff0c;小程序插件就是可被添加到小程序内直接使用的功能组件。插件依附于主程序的辅助程序&a…

详解c++---string的介绍(上)

这里写目录标题什么是stringstring的构造函数string的赋值重载string的遍历第一种方式 [ ]第二种方式 范围for第三种方式 正向迭代器反向迭代器string中的capacitysize lengthmax_sizecapacityreserveresizeshrink_to_fitstring的element access什么是string 那这里大家就只用…