C++Primer 第五版 ——《第十一章 》关联容器

目录

Preface

使用关联容器

定义关联容器 以及关联容器支持的操作(376P)

初始化 multimap 或 multiset ( 377P)

有序容器的关键字类型的限制 ( 378P)

pair 标准库类型 ( 379P )

pair 类型提供的操作(380P)

使用 pair 类型作为函数的返回值类型  以及 使用 make_pair 来创建 pair对象(380P)

关联容器支持的操作(381P)

关联容器的迭代器 ( 382P )

set 的迭代器是 const的 ( 382P)

使用 begin 和  end 操作来遍历关联容器 ( 382P )

关联容器通常不应该使用泛型算法( 383P)

使用关联容器的 insert 和 emplace 成员  添加元素 ( 384P)

检测 insert 的返回值 ( 385P)

向 multiset 和  multimap 添加元素 ( 386P)

使用 erase 成员 删除元素 ( 386P)

unordered_map 和 map 提供下标操作 、at函数 ( 387P )

使用 unordered_map 和 map 下标操作的返回值(388P)

使用 find、count、lower_bound、upper_bound、equal_range 访问元素 (388P)

使用 map 和 unordered_map 的find 操作 代替 下标操作(389P)

在 multimap 或 multiset 中查找元素

使用 lower_bound 和 upper_bound 操作( 389P、390P )

equal_range 函数( 391P)

无序容器 (394P)

无序容器支持的操作 395P )


Preface


关联容器和顺序容器的主要区别是:

  •  关联容器中的元素是按关键字来保存和访问的。
  • 顺序容器中的元素是按它们在容器中的位置来顺序保存和访问的。

关联容器的特点有:

  •   关联容器有一个特点是通过关键字来高效地访问和查找元素。
  • map 中的元素是一些关键字,然后这些关键字映射到一个值, 关键字起到索引的作用, 值则表示索引引用的元素数据。
  • set 中每个元素都是一个关键字; set 支持高效的查询关键字操作, 比如说查找一个给定的关键字是否在 set 中。
  • 类型 map 和 multimap 定义在头文件 map 中;
  • set 和 multiset 定义在头文件 set 中;
  • 无序容器则定义在头文件 unordered_map 和unordered_set 中。

使用关联容器


map 是关键字 - 值对的集合,所以 被通常被称为关联数组,与正常数组的不同在于:

  • 下标不必是整数 我们是通过一个关键字 而不是 整数下标来查找值。例如:将一个人的名字作为关键字,将其电话号码作为值。
  •   那么 set 就是 关键字的简单集合,set 中没有值。  当只是想知道一个值是否存在时, set 是最有用的。

使用 map:

int main()
{
	std::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)
	{
		cout << w.first << " 出现 " << w.second << ((w.second > 1) ? " times" : " time") << endl;
	}
	
	system("pause");
	return 0;
}

输出结果为:

huang
cheng
huang
cjemh
cheng
^Z
cheng 出现 2 times
cjemh 出现 1 time
huang 出现 2 times
  • 注意:  定义一个map , 我们必须指定关键字和值的类型。上述程序中map 保存的,每个元素, 关键字是string类型,值是 size_t 类型。
  • 但我们从一个 map 中获取一个元素时,会得到一个pair 类型的对象( 说明获取到的这个元素是 pair 类型的)。pair 是一个模板类型,其中包含了两个名为 first 和 second 的公有成员数据。
  • map 所使用的 pair 用 first 成员保存关键字, 用 second 成员保存其关键字对应的值。

使用 set:

int main()
{
	//统计输入中每个单词出现的次数
	std::map<string, size_t> word_count; // 从 string 到 size_t 的空map
	std::set<string> exclude = { "The", "But", "And", "Or", "An", "A",
	                             "the", "but", "and", "or", "an", "a" };
	string word;
	while (cin >> word)
	{
		
		if (exclude.find(word) == exclude.end())
		{
			++word_count[word]; 
		}
	}
	
	system("pause");
	return 0;
}

输出结果为:

huang
chengt
the
or
and
tao
^Z
chengt 出现 1 time
huang 出现 1 time
tao 出现 1 time
  • 注意: 定义一个set , 必须指定其元素类型, 本例中元素类型是string. 
  • 我们可以对所有的关联容器中的元素,进行列表初始化。

练习题11.1:描述 map 和 vector的不同:

  • map 属于关联容器;vector 属于 顺序容器
  • map 保存的是键值对的集合,它的下标不需要是整数; 而 vector 是某种类型的集合,它的下标需要是整数
  • map中的元素是按 关键字来保存和访问的;vector 是以它们在容器中的位置来顺序保存和访问的。

定义关联容器 以及关联容器支持的操作(376P)


关联容器,不管是有序的还是无须的,都支持下图中普通的容器操作。

关联容器不支持的操作有:

  •  关联容器不支持顺序容器位置相关的操作,例如 push_front 或push_back。因为关联容器中的元素是根据关键字存储的。
  • 此外,关联容器不支持构造函数或插入操作,这些操作接受元素值和数量值的操作。
  • 关联容器除了跟顺序容器支持的一样的操作(上述的图片操作)外,还支持顺序容器不支持的操作 和 类型别名。
  • 关联容器的迭代器都是双向的
  •  每个关联容器都定义了一个默认的构造函数,它创建了一个指定类型的空容器。

下面的程序使用了  列表初始化和 一个值范围内的元素 来初始化 set 和 map  容器:

int main()
{

	// list initialization
	std::set<string> exclude = { "the", "but", "and", "or", "an", "a",
							   "The", "But", "And", "Or", "An", "A" };
	// 三个元素; authors将姓映射为名
	std::map<string, string> authors = { {"Joyce", "James"},
										 {"Austen", "Jane"},
										 {"Dickens", "Charles"} };

	std::map<string, string> word_count = authors; //用一个map 初始化另一个map,只要这些值可以转换为容器中元素的类型。 map 的元素类型是 string
	cout << "输出map 容器 word_count 中所有的键值-对:\n";
	for (const auto &w : word_count)
	{
		cout << w.first << " 对应" << w.second << endl;
	}
	cout << endl;
	std::set<string> exclude1(exclude.cbegin(), exclude.cend()); // 用一个值范围初始化化关联容器。 set 的元素类型就是关键字类型
	cout << "输出set 容器 exclude1 中所有的关键字元素:\n";
	for (auto tt : exclude1)
	{
		cout << tt << " ";
	}
	cout << endl;
	system("pause");
	return 0;
}

输出结果为:

输出map 容器 word_count 中所有的键值-对:
Austen 对应Jane
Dickens 对应Charles
Joyce 对应James

输出set 容器 exclude1 中所有的关键字元素:
A An And But Or The a an and but or the
  • 不管是 map  还是  set 中, 其关键字必须是唯一的,不可重复。

初始化 multimap 或 multiset ( 377P)


  •  对于set 和 map  容器 ,  关键字只能一一对应一个元素值。 但是multimap 和 multiset 一个关键字可以对应不同的元素值, 但是注意的是一个元素值不可以对应多个关键字。

下面的程序演示了具有唯一关键字的 set 容器与 允许重复关键字的multiset 容器之间的区别:

int main()
{
	vector<int> ivec;
	for (vector<int>::size_type i = 0; i != 10; ++i)
	{
		ivec.push_back(i);
		ivec.push_back(i);
	}
	std::set<int> iset(ivec.cbegin(), ivec.cend());
	std::multiset<int> miset(ivec.cbegin(), ivec.cend());

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

	cout << "输出 set 容器中的所有元素:" << endl;
	for_each(iset.cbegin(), iset.cend(), [](int tt) {cout << tt << " "; });
	cout << endl;
	cout << "输出 multiset 容器中的所有元素:" << endl;
	for_each(miset.cbegin(), miset.cend(), [](int tt) {cout << tt << " "; });

	system("pause");
	return 0;
}
输出结果为:
20
10
20

输出 set 容器中的所有元素:
0 1 2 3 4 5 6 7 8 9

输出 multiset 容器中的所有元素:
0 0 1 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8 9 9 

练习题11.5:

  • map 是 键值对的集合;set 是简单的关键字集合
  • 在定义 map 的时候,必须提供 关键字类型和值类型; 在定义set 的时候只需要提供关键字类型, set的元素值类型就是关键字类型
  • 当需要查找给定值所对应的数据时, 应使用map, 其中保存的是 < 关键字, 值> 对, 可以按关键访问值。
  • 如果只需判定给定值是否存在时, 应使用set, 它是简单的值的集合。

练习题11.6:

  • 两者都可以保存元素集合。
  • 如果只需要顺序访问这些元素,或是按位置访问元素, 那么应使用list.
  • 如果需要快速判定是否有元素等于给定值, 则应使用set.

有序容器的关键字类型的限制 ( 378P)


 map, multimap, set, and multiset 这些容器的关键字类型必须定义了 比较元素的方法来比较元素。default,标准库使用关键字类型的 “ < ” 运算符来比较两个关键字。

我们也可以提供自定义的操作来代替关键字类型上 < 运算符。但是提供的该操作必须在关键字类型上定义一个严格弱序。可以将严格弱序看作是 “小于等于 ”。

传递给排序泛型算法的可调用对象,必须跟该关联容器中关键字的类型一样。


pair 标准库类型 ( 379P )


  • 该标准库类型定义在头文件 utility 中。一个pair 保存两个 public 数据成员( first 和 second )类似于容器,pair也是一个模板,我们可以创建该类型的实例。
  • 当我们定义一个 pair 时, 必须提供两个类型名( 两个类型可以相同或不同),然后 pair 的first 和 second 数据成员将具有对应的类型。

 实例化 pair 类型:

int main()
{
	std::pair<string, string> anon; // holds two strings,
	std::pair<string, size_t> word_count; // holds a string and an size_t
	std::pair<string, vector<int>> line; // holds string and vector<int>
	// std::pair<string, string> author(" huang", "cheng", "tao"); 错误, pair 的构造函数不接受2个以上的参数

	std::pair<string, string> author{ " huang", "cheng" }; // first 数据成员初始化为 huang , second 初始化为 cheng
	cout << "输出 author 的first 的值:" << author.first << " , 输出author 的 second 的值:" << author.second << endl;

	std::pair<string, string> vec(" tao", "taotao");
	cout << "输出 vec 的first 的值:" << vec.first << " , 输出 vec 的 second 的值:" << vec.second << endl;

	std::pair<string, string> ve = { " taohuang", "taotttao" };
	cout << "输出 ve 的first 的值:" << ve.first << " , 输出 ve 的 second 的值:" << ve.second << endl;

	auto tt = make_pair(10, 20); // tt 的类型是 从 10 和 20 的类型推断出来的
	cout << "输出 tt 的first 的值:" << tt.first << " , 
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值