C++标准模板库(STL)(3) 之 vector map容器快速学习 (自学笔记)

目录

一、简介

1.1 STL 具有两个特点

1.2 举一个数组对比 STL 的例子,以说明STL特点和优势

1.3 STL中六大组件

1.4 C++ STL头文件

二、容器

三、序列式容器

3.1 序列式容器对比(部分)

3.2 vector(向量)容器

3.2.1 定义方法

3.2.2基本函数实现

3.2.3 实践

四、关联式容器

4.1 关联式容器对比

4.1.1 set和map的特性、区别

4.2 map容器

4.2.1 定义方法

4.2.2 基本操作函数

4.2.3 实践

附录:摘录及参考资料


本文的目标是:在没有STL学习基础的情况下,快速了解vector和map两个容器的定义和使用。主要介绍了STL定义、组件,STL组件中容器的分类及其特点,并进一步引出vector容器和map容器,介绍了它们的基本特点和使用方法。

一、简介

STL(Standard Template Library),即标准模板库,是一个具有工业强度的,高效的C++程序库。它被容纳于C++标准程序库(C++ Standard Library)中,是ANSI/ISO C++标准中最新的也是极具革命性的一部分。其包含有大量的模板类和模板函数,是 C++ 提供的一个基础模板的集合,用于完成诸如输入/输出、数学计算等功能。

 

1.1 STL 具有两个特点

一是数据结构和算法的分离,二是不是面向对象的。

数据结构和算法的分离。使得STL变得非常通用。例如,由于STL的sort()函数是完全通用的,你可以用它来操作几乎任何数据集合,包括链表,容器和数组。

不是面向对象的。为了具有足够通用性,STL主要依赖于模板,而不依赖封装、继承、虚函数(面向对象编程的三要素)。使得STL的组件具有广泛通用性的底层特征。另外,由于STL是基于模板,内联函数的使用使得生成的代码短小高效。

 

1.2 举一个数组对比 STL 的例子,以说明STL特点和优势

在 C++ 中如果定义一个数组,可采用:

int a[n];

这种定义数组的方法需要事先确定好数组的长度,即 n 必须为常量,这意味着,如果在无法确定数组长度,就需要将数组长度设为可能的最大值,导致存储空间的浪费。

当然,还可以采用在堆空间中动态申请内存的方法,根据变量n动态申请内存,防止存储空间浪费的问题。但是,如果程序执行过程中出现空间不足的情况时,则需要加大存储空间,将原内存空间的数据全部复制到新申请的内存空间中,并将原来的堆空间释放:

int *p = new int[n];

...

//发现空间不足
int * temp = new int[m];
memecpy(temp, p, sizeof(int)*n));
delete [] p; p = temp;

而完成相同的操作,如果采用 STL 标准库,则会简单很多,因为大多数操作细节将不需要程序员关心。下面是使用向量模板类 vector 实现以上功能的示例:

vector <int> a; //定义a数组,当前数组长度为0。

//向数组a中添加 10 个元素,a可以根据存储数据的数量自动变长。
for (int i = 0; i < 10 ; i++)
{    
    a.push_back(i);
}


//可以手动调整数组 a 的大小
a.resize(100);
a[90] = 100;

//可以直接删除数组 a 中所有的元素,a的长度变为0。
a.clear();

//重新调整 a 的大小为 20,并存储 20 个 -1 元素。
a.resize(20, -1);

对比以上两种使用数组的方式不难看出,使用 STL 可以更加方便灵活地处理数据。所以,大家只需要系统地学习 STL,便可以集中精力去实现程序的功能,而无需再纠结某些细节如何用代码实现。

 

1.3 STL中六大组件

容器(Container):一些封装数据结构的模板类,如list列表容器,vector向量容器,deques等。为了访问容器中的数据,可以使用由容器类输出的迭代器。

迭代器(Iterator):提供了访问容器中对象的方法,如同指针。对容器中数据的读和写,是通过迭代器完成的。事实上,C++的指针也是一种迭代器。迭代器也可以是那些定义了operator*(),或定义了类似于指针操作符方法的类对象;

算法(Algorithm):是用来操作容器中的数据的模板函数。例如,STL用sort()来对一个vector中的数据进行排序,用find()来搜索一个list中的对象,函数本身与他们操作的数据的结构和类型无关,因此他们可以在从简单数组到高度复杂容器的任何数据结构上使用。这些算法在 std 命名空间中定义,其中大部分算法都包含在头文件 <algorithm> 中,少部分位于头文件 <numeric> 中。

函数对象(Functor):如果一个类将()运算符重载为成员函数,这个类就称为函数对象类,这个类的对象就是函数对象(又称仿函数)。

适配器(Adaptor):可以使一个类的接口(模板的参数)适配成用户指定的形式,从而让原本不能在一起工作的两个类工作在一起。值得一提的是,容器、迭代器和函数都有适配器。

分配器(allocator):为容器类模板提供自定义的内存申请和释放功能,由于往往只有高级用户才有改变内存分配策略的需求,因此内存分配器对于一般用户来说,并不常用。

其中,迭代器、函数对象、适配器、内存分配器这4部分是为容器、算法服务的。但是,STL最重要的是要理解前三个组件:容器、迭代器、算法。

 

1.4 C++ STL头文件

迭代器头文件:<iterator>

算法头文件:<algorithm>

序列式容器头文件:<vector>、<deque>、<list>、<queue>、<stack>

关联式容器头文件:<set>、<map>

无序关联式容器:<unordered_set>、<unordered_map>

其它:<functional>、<numeric>、<memory>、<utility>

按照 C++ 标准库的规定,所有标准头文件都不再有扩展名。以 <vector> 为例,此为无扩展名的形式,而 <vector.h> 为有扩展名的形式。

 

二、容器

它就是一些模板类的集合,但和普通模板类不同的是,容器中封装的是组织数据的方法(也就是数据结构)。

容器类型是可用于创建具体容器对象的模板。以前有11个容器类型:deque、list、queue、priority_queue、stack、vector、map、multimap、set、multiset、bitest。C++11新增了5种容器类型:forward_list、unordered_map、unordered_multimap、unordered_set、unordered_multiset,并且不将bitset视为容器,而将其视作一种独立的类别。

因此,除了bitset之外,目前共有15种容器类型,他们又可以分为3个大的种类,分别是:序列式容器(sequence containers)、关联式容器(associative containers)、无序关联式容器(哈希容器)。大概介绍为:

(1)序列式容器(sequence containers)

序列式容器共包括7个类型的容器:vector、deque、list、forward_list、queue、priority_queue、stack。

之所以被称为序列容器,是因为它们都以线性排列(类似普通数组的存储方式)来存储某一指定类型(例如int、double等)的数据,且元素在容器中的位置同元素的值无关,即容器不是排序的。将元素插入容器时,指定在什么位置,元素就会位于什么位置。

(2)关联式容器(associative containers)

关联式容器提供了4个类型的容器:set、multiset、map、multimap。

使用关联式容器存储的元素,都是一个一个的“键值对”( <key,value> )。排序容器中的元素默认是由小到大排序好的,即便是插入元素,元素也会插入到适当位置。所以关联容器在查找时具有非常好的性能。

(3)无序关联式容器(哈希容器)。

无序关联式容器提供了4个具体的容器:unordered_map、unordered_multimap、unordered_set、unordered_multiset。

它们都是C++11才引入到STL标准库中的。和排序容器不同,无序关联式容器中的元素是未排序的,元素的位置由哈希函数确定。无序关联式容器擅长通过指定键查找对应的值。

这3类容器的存储方式完全不同,因此使用不同容器完成相同操作的效率也大不相同。所以在实际使用时,要善于根据想实现的功能,选择合适的容器。

 

三、序列式容器

3.1 序列式容器对比(部分)

序列式容器有7种,这里我们比较它们其中的4种:

vector<T>(向量容器):用来存放 T 类型的元素,是一个长度可变的序列容器,即在存储空间不足时,会自动申请更多的内存。使用此容器,在尾部增加或删除元素的效率最高(时间复杂度为 O(1) 常数阶),在其它位置插入或删除元素效率较差(时间复杂度为 O(n) 线性阶,其中 n 为容器中元素的个数);

deque<T>(双端队列容器):和 vector 非常相似,区别在于使用该容器不仅尾部插入和删除元素高效,在头部插入或删除元素也同样高效,时间复杂度都是 O(1) 常数阶,但是在容器中某一位置处插入或删除元素,时间复杂度为 O(n) 线性阶;

list<T>(链表容器):是一个长度可变的、由 T 类型元素组成的序列,它以双向链表的形式组织元素,在这个序列的任何地方都可以高效地增加或删除元素(时间复杂度都为常数阶 O(1)),但访问容器中任意元素的速度要比前三种容器慢,这是因为 list<T> 必须从第一个元素或最后一个元素开始访问,需要沿着链表移动,直到到达想要的元素。

forward_list<T>(正向链表容器):和 list 容器非常类似,只不过它以单链表的形式组织元素,它内部的元素只能从第一个元素开始访问,是一类比链表容器快、更节省内存的容器。

 

3.2 vector(向量)容器

vector 容器是 STL 中最常用的容器之一,是对 C++ 普通数组的“升级版”。 vector 实现的是一个动态数组,即可以进行元素的插入和删除,在此过程中,vector 会(通过new和delete)动态调整所占用的内存空间,整个过程无需人工干预。

因为该容器擅长在尾部插入或删除元素,在常量时间内就可以完成,时间复杂度为O(1);而对于在容器头部或者中部插入或删除元素,则花费时间要长一些(移动元素需要耗费时间),时间复杂度为线性阶O(n)。

3.2.1 定义方法

std::vector<int> a1;               //1个元素,默认初始值为0
std::vector<int> a2(10);           //10个元素,默认初始值为0
std::vector<int> a3 {2, 3, 5, 7};  //4个元素,指定元素值
std::vector<int> a4(10, 100);      //10个元素,默认初始值重新指定为100

int num = 20;
double value = 1.0;
std::vector<double> a5(num, value) //可以用变量指定元素个数和初始值等信息

std::vector<char> a6_temp(5, 'c');
std::vector<char> a6(a6_temp);      //用其它元素类型相同的vector,创建新的vector容器。

int arr[] = {1, 2, 3, 4, 5};
std::vector<int> a7(arr, arr+3);   //a7将保存{1, 2, 3}

std::vector<int> a8_temp {2, 3, 5, 7, 11, 13};
std::vector<int> a8(std::begin(a8_temp), std::end(a8_temp)-2); //a8将保存{2, 3, 5, 7}


3.2.2基本函数实现

(1) 构造函数

vector():创建一个空vector

vector(int nSize):创建一个vector,元素个数为nSize

vector(int nSize,const T& t):创建一个vector,元素个数为nSize,且元素值均为t

vector(const vector&):复制构造函数

vector(begin,end):复制[begin,end)区间内另一个数组的元素到vector中

(2) 增加函数

void push_back(const T& x):向量尾部增加一个元素X

iterator insert(iterator it,const T& x):向量中迭代器指向元素前增加一个元素x

iterator insert(iterator it,int n,const T& x):向量中迭代器指向元素前增加n个相同的元素x

iterator insert(iterator it,const_iterator first,const_iterator last):向量中迭代器指向元素前插入另一个相同类型向量的[first,last)间的数据

(3) 删除函数

iterator erase(iterator it):删除向量中迭代器指向元素

iterator erase(iterator first,iterator last):删除向量中[first,last)中元素

void pop_back():删除向量中最后一个元素

void clear():清空向量中所有元素

(4) 遍历函数

reference at(int pos):返回pos位置元素的引用

reference front():返回首元素的引用

reference back():返回尾元素的引用

iterator begin():返回向量头指针,指向第一个元素

iterator end():返回向量尾指针,指向向量最后一个元素的下一个位置

reverse_iterator rbegin():反向迭代器,指向最后一个元素

reverse_iterator rend():反向迭代器,指向第一个元素之前的位置

(5) 判断函数

bool empty() const:判断向量是否为空,若为空,则向量中无元素

(6) 大小函数

int size() const:返回向量中元素的个数

int capacity() const:返回当前向量张红所能容纳的最大元素值

int max_size() const:返回最大可允许的vector元素数量值

(7) 其他函数

void swap(vector&):交换两个同类型向量的数据

void assign(int n,const T& x):设置向量中第n个元素的值为x

void assign(const_iterator first,const_iterator last):向量中[first,last)中元素设置成当前向量元素

3.2.3 实践

(1)基本函数操作

/*************************************************************************
	> File Name: vector_1.cpp
	> Author: hank
	> Mail: 34392195@qq.com 
	> Created Time: 2020年08月07日 星期五 05时27分21秒
 ************************************************************************/

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

int main(int argc, char *argv[])
{
//1 创建并初始化vector
	vector<int> obj{2, 3, 4, 15, 1, 7, 3};	

//2-1 遍历方法1(size()数据个数)
	cout << "两种遍历方法:" << endl;
    for(int i = 0; i < obj.size(); i++)//size()容器中实际数据个数 
    {
        cout<< obj[i] <<" ";
    }
	cout << endl;

//2-2 遍历方法2(使用迭代器)
	vector<int>::iterator iter;
	for(iter = obj.begin(); iter != obj.end(); iter++)
	{
		cout << *iter << " ";
	}
	cout << endl;

	cout << endl;
	
//3-1 从小到大排序(sort(),需要<algorithm>算法头文件)
	obj.push_back(5); //插入
	obj.push_back(3); //插入
	
	cout << "添加两个数据:" << endl;
	for(iter = obj.begin(); iter != obj.end(); iter++)
	{
		cout << *iter << " ";
	}
	cout << endl << endl;
	
	sort(obj.begin(), obj.end());//sort()排序
	
	cout << "sort()排序后:" << endl;
	for(iter = obj.begin(); iter != obj.end(); iter++)
	{
		cout << *iter << " ";
	}
	cout << endl;
	

//3-2 从大到小排序(reverse(),需要<algorithm>算法头文件)	
	reverse(obj.begin(), obj.end()); //reverse()排序
	
	cout << "reverse()排序后:" << endl;
	for(iter = obj.begin(); iter != obj.end(); iter++)
	{
		cout << *iter << " ";
	}
	cout << endl << endl;

//4 删除(pop_back() 尾删)
    for(int i = 0; i < 4; i++) 
    {
        obj.pop_back();
    }
 
	cout << "删除4个元素后:" << endl;
	for(iter = obj.begin(); iter != obj.end(); iter++)
	{
		cout << *iter << " ";
	}
 	cout << endl << endl;

//6 清空(clear())
	obj.clear();
	
	cout << "清空后:" << endl;
	for(iter = obj.begin(); iter != obj.end(); iter++)
	{
		cout << *iter << " ";
	}
	cout << endl;

	return 0;
}

编译执行:

 

(2)C++ Primer Plus P683 程序清单16.9 vect3.app源代码

/*************************************************************************
	> File Name: vector_2.cpp
	> Author: hank
	> Mail: 34392195@qq.com 
	> Created Time: 2020年07月28日 星期二 20时27分51秒
 ************************************************************************/

#include<iostream>
#include<vector>
#include<string>
#include<algorithm>
using namespace std;

struct Review
{
	string title;
	int rating;
};

//function1: 升序-sort版本1,运算符<重载多实现
bool operator<(const Review &r1, const Review &r2)
{
	if(r1.title < r2.title)
		return true;
	else if(r1.title == r2.title && r1.rating < r2.rating)
		return true;
	else
		return false;
}

//function2: 降序-sort版本2,定义函数对象
bool worseThan(const Review &r1, const Review &r2)
{
	if(r1.rating > r2.rating)
		return true;
	else
		return false;
}

//function3: 输入数据
bool FillReview(Review &rr)
{
	//rr.title
	cout << "Enter book title (enter \"quit\" to quit): ";
	getline(cin, rr.title);
	if(rr.title == "quit")
	{
		return false;
	}

	//rr.rating
	cout << "Enter book rating: ";
	cin >> rr.rating;
	if(!cin)
	{
		return false;
	}

	//clear input buff
	while(cin.get() != '\n') 
	{
		continue;
	}

	return true;
}

//function4: 遍历打印
void ShowReview(const Review &rr)
{
	cout << rr.rating << "\t" << rr.title << endl;
}

//**auto input
vector<Review> auto_FillReview(vector<Review> books)
{
	Review temp;
	
	temp.title = "The Cat Who Can Teach You Weight Loss";
	temp.rating = 8;
	books.push_back(temp);

	temp.title = "The Dogs of Dharma";
	temp.rating = 6;
	books.push_back(temp);
	
	temp.title = "The Wimps of Wonk";
	temp.rating = 3;
	books.push_back(temp);

	temp.title = "Farewell and Delete";
	temp.rating = 7;
	books.push_back(temp);
	
	return books;
}

//main
int main()
{
	vector<Review> books;
	Review temp;

	//input data untile input "quit"
#if 0/* 书上code,终端输入数据 */
	while (FillReview(temp))
		books.push_back(temp);
#else/* 写定函数,自动输入 */
	books = auto_FillReview(books); 
#endif

	if (books.size() > 0)
	{
		//original datas
		cout << "Thank you. You entered the following "
			<< books.size() 
			<< " ratings:\n"
			<< "Rating\tBook\n";
		for_each(books.begin(), books.end(), ShowReview);
		cout << endl;

		//sort by title, ascending
		sort(books.begin(), books.end());
		cout << "Sort by title:\n" 
			<<"Rating\tBook\n";
		for_each(books.begin(), books.end(), ShowReview);
		cout << endl;
		
		//sort by rating, descending
		sort(books.begin(), books.end(), worseThan);
		cout << "Sort by rating:\n"
			<<"Rating\tBook\n";
		for_each(books.begin(), books.end(),ShowReview);
		cout << endl;

		//random
		random_shuffle(books.begin(), books.end());
		cout << "After shuffling:\n"
			<< "Rating\ttitle\n";
		for_each(books.begin(), books.end(), ShowReview);
	}
	else
	{
		cout << "No entries! Bye!" << endl;
	}
	return 0;

}

编译执行:

 

四、关联式容器

关联是容器再存储元素值的同时,还会为各个元素再配备一个“键”(Key)值。key的功能是在使用关联式容器的过程中,如果查找的目标元素的键值已知,就可以通过键值直接找到该元素,无需再遍历整个容器。

相比于序列式容器,选用关联式容器往往是因为,关联式容器可以快速查找、读取或者删除所存储的元素。同时,插入元素的效率也比序列式容器高。除此之外,序列式容器中存储的元素默认都是未经过排序的,而使用关联式容器存储的元素,默认会键值升序排序

注意,关联式容器所具备的这些特性,归咎于 STL 标准库在实现该类型容器时,底层选用了 「红黑树」这种数据结构来组织和存储各个键值对。

4.1 关联式容器对比

C++ STL 标准库提供了4种关联式容器,分别为 map、set、multimap、multiset。

表4.1 关联式容器特点对比
容器名称特点
set定义在 <set> 头文件中,使用该容器存储的数据,各个元素键和值完全相同且各个元素的值不能重复(保证了各元素键的唯一性)。该容器会自动根据各个元素的键(其实也就是元素值)的大小进行升序排序(调用 std::less<T>)。
multiset定义在 <set> 头文件中,和 set 容器唯一的不同在于,multiset 容器中存储元素的值可以重复(一旦值重复,则意味着键也是重复的)。
map定义在 <map> 头文件中,使用该容器存储的数据,其各个元素的键必须是唯一的(即不能重复),该容器会根据各元素键的大小,默认进行升序排序(调用 std::less<T>)。
multimap定义在 <map> 头文件中,和 map 容器唯一的不同在于,multimap 容器中存储元素的键可以重复。

4.1.1 set和map的特性、区别

set特性:

  • set中的元素键值和实值相同,即value就是key
  • 不允许出现键值重复(multiset允许键值重复)
  • 所有的元素都会被自动排序(升序)
  • 不能通过迭代器来改变set的值,因为set的值就是键
  • 搜索、移除和插入拥有对数复杂度
  • 当对容器中的元素进行插入或者删除时,操作之前的所有迭代器在操作之后依然有效。
  • set集合中的元素为结构体或自定义类时,该结构体或自定义类必须实现运算符‘<’的重载。

map特性:

  • map中的元素键值和实值可以不相同
  • 和set一样,map中不允许出现键值重复(multimap允许键值重复)(因此,map支持[ ]运算符,multimap不支持[ ]运算符)
  • 和set一样,所有的元素都会通过键值自动排序(升序)
  • map的键是不能修改的,但是其键对应的值是可以修改的。

map和set一样是关联式容器,它们的底层容器都是红黑树,区别就在于map的值不作为键,键和值是分开的,而set键和值相同。

4.2 map容器

map 由红黑树实现,其元素都是 “键/值” 所形成的一个对组(key/value pairs)。每个元素有一个键,一个键只能出现一次,不允许重复。 map 内部所有元素按照“键”自动排序。

map 主要用于键和值一对一映射的情况,比如一个班级中,每个学生的学号跟他的姓名就存在着一对一映射的关系。

根据 key 值快速查找记录,查找的复杂度基本是 O(logN),如果有 1000 个记录,二分查找最多查找 10次(1024)。增加和删除节点对迭代器的影响很小,除了那个操作节点,对其他的节点都没有什么影响。

4.2.1 定义方法

std::map<std::string, int>a1;  //调用构造函数
std::map<std::string, int>a2{{"string 1",10},{"string 2",20}};  //初始化
std::map<std::string, int>a3{std::make_pair("string 1",10),std::make_pair("string 2",20)}; //map容器中存储的键值对,本质是pair类模板创建的pair对象。

std::map<std::string, int>temp_a4;
std::map<std::string, int>a4(temp_a4); //map拷贝构造函数

std::map<std::string, int>fun_makeMap() //移动构造函数,C++11标准中
{
    std::map<std::string, int>temp_a5{{“string 1”, 10}, {"string 2", 20}};
    return temp_a5;
}
std::map<std::string, int>a5(fun_makeMap());

//取已建map容器中指定区域内的键值对,创建并初始化map容器
//这里初始化了一个{“string 2”, 20}键值对的容器
std::map<std::string, int>temp_a6{{"string 1", 10}, {"string 2", 20}};
std::map<std::string, int>a6(++temp_a6.begin(), temp_a6.end());


//修改map容器排序规则
//    第一个调用std::less<T>为升序排序。(这是默认规则)
//    第二个调用std::greater<std::string>为降序排序。
std::map<std::string, int, std::less<std::string> >a7_1{{"string 1", 10}, {string2, 20}}
std::map<std::string, int, std::greater<std::string> >a7_2{{"string 1", 10}, {"string 2", 20}};

4.2.2 基本操作函数

begin():返回指向map头部的迭代器。返回指向容器中第一个(注意,是已排好序的第一个)键值对的双向迭代器。如果 map 容器用 const 限定,则该方法返回的是 const 类型的双向迭代器。

end():返回指向map末尾的迭代器。返回指向容器最后一个元素(注意,是已排好序的最后一个)所在位置后一个位置的双向迭代器,通常和 begin() 结合使用。如果 map 容器用 const 限定,则该方法返回的是 const 类型的双向迭代器。

rbegin():返回一个指向map尾部的逆向迭代器。返回指向最后一个(注意,是已排好序的最后一个)元素的反向双向迭代器。如果 map 容器用 const 限定,则该方法返回的是 const 类型的反向双向迭代器。

rend():返回一个指向map头部的逆向迭代器。返回指向第一个(注意,是已排好序的第一个)元素所在位置前一个位置的反向双向迭代器。如果 map 容器用 const 限定,则该方法返回的是 const 类型的反向双向迭代器。

cbegin():和 begin() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改容器内存储的键值对。

cend():和 end() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改容器内存储的键值对。

crbegin():和 rbegin() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改容器内存储的键值对。

crend():和 rend() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改容器内存储的键值对。

find(key):查找一个元素。在 map 容器中查找键为 key 的键值对,如果成功找到,则返回指向该键值对的双向迭代器;反之,则返回和 end() 方法一样的迭代器。另外,如果 map 容器用 const 限定,则该方法返回的是 const 类型的双向迭代器。

lower_bound(key):返回键值>=给定元素的第一个位置。返回一个指向当前 map 容器中第一个大于或等于 key 的键值对的双向迭代器。如果 map 容器用 const 限定,则该方法返回的是 const 类型的双向迭代器。

upper_bound(key):返回键值>给定元素的第一个位置。返回一个指向当前 map 容器中第一个大于 key 的键值对的迭代器。如果 map 容器用 const 限定,则该方法返回的是 const 类型的双向迭代器。

equal_range(key):该方法返回一个 pair 对象(包含 2 个双向迭代器),其中 pair.first 和 lower_bound() 方法的返回值等价,pair.second 和 upper_bound() 方法的返回值等价。也就是说,该方法将返回一个范围,该范围中包含的键为 key 的键值对(map 容器键值对唯一,因此该范围最多包含一个键值对)。

empty() :如果map为空则返回true;否则 false。

max_size():返回可以容纳的最大元素个数

size():返回map中元素的个数

at(key):找到 map 容器中 key 键对应的值,如果找不到,该函数会引发 out_of_range 异常。

insert():插入元素,插入键值对。

emplace():在当前 map 容器中的指定位置处构造新键值对。其效果和插入键值对一样,但效率更高。c++11

erase():删除一个元素。删除 map 容器指定位置、指定键(key)值或者指定区域内的键值对。

clear():删除所有元素。清空 map 容器中所有的键值对,即使 map 容器的 size() 为 0。

swap():交换两个map。这要求操作的 2 个键值对的类型必须相同。

count():返回指定元素出现的次数。

get_allocator():返回map的配置器。

key_comp():返回比较元素key的函数。

value_comp():返回比较元素value的函数。

4.2.3 实践

使用一些基本的方法,完成一些操作。

/*************************************************************************
	> File Name: map1.cpp
	> Author: hank
	> Mail: 34392195@qq.com 
	> Created Time: 2020年08月05日 星期三 01时22分24秒
 ************************************************************************/

#include <iostream>
#include <map>
#include <string>
using namespace std;

int main(int argc, char *argv[])
{
//1.定义
	map<int, string, greater<int>> id_name; //greater:倒序
	map<int, string>::iterator iter;//迭代器


//2.插入
#if 0
	// 插入单个键值对,并返回插入位置和成功标志,插入位置已经存在值时,插入失败
	pair<iterator,bool> insert (const value_type& val);
	 
	//在指定位置插入,在不同位置插入效率是不一样的,因为涉及到重排
	iterator insert (const_iterator position, const value_type& val);
	 
	// 插入多个
	void insert (InputIterator first, InputIterator last);
	 
	//c++11开始支持,使用列表插入多个   
	void insert (initializer_list<value_type> il);
#endif

	id_name.insert(pair<int, string>(27, "Hank"));				//插入pair数据
	id_name.insert(map<int, string>::value_type(0, "Baby"));	//插入value_type数据
	id_name.emplace(28, "Leiney");			
	
	pair<map<int, string>::iterator, bool> ret;
	ret = id_name.insert(map<int, string>::value_type(0, "another Baby"));	//插入  c++11
	if(ret.second == false)
	{
		cout << "element '0' alread existed, with a value of " << ret.first->second << endl;
		cout << endl;  
	}

	//列表形式插入
	map<int, string> id_name2; 
	id_name2.insert(
	{
		{22, "laoluo"}, 
		{37, "laoguo"}
	}
	);

	//数组形式插入,id自动生成,从1开始
	map<int, string> id_name3;
	id_name3[1] = "jiaojiao";
	id_name3[2] = "xiaoming";
	id_name3[3] = "xiaohui";



//3.大小
	cout << "size(id_name) = " << id_name.size() << endl;
	cout << endl;

//4.判空,迭代器遍历
	if(id_name.empty())
	{
		cout << "map is empty!" << endl;
	}
	else
	{
		for(iter = id_name.begin(); iter != id_name.end(); iter++)
		{
			cout << iter->first << ":" << iter->second << ";";
		}
		cout << endl;
	}

	for(iter = id_name2.begin(); iter != id_name2.end(); iter++)
	{
		cout << iter->first << ":" << iter->second << ";";
	}
	cout << endl;

	for(iter = id_name3.begin(); iter != id_name3.end(); iter++)
	{
		cout << iter->first << ":" << iter->second << ";";
	}
	cout << endl << endl;


//5.查找
#if 0
	// 关键字查询,找到则返回指向该关键字的迭代器,否则返回指向end的迭代器
	// 根据map的类型,返回的迭代器为 iterator 或者 const_iterator
	iterator find (const key_type& k);
	const_iterator find (const key_type& k) const;
#endif
	int find_key = 28;  //改为1111什么的试一下

	if(id_name.end() == (iter = id_name.find(find_key)))
	{
		cout << "not find out data with key = " << find_key << endl;
	}
	else
	{
		cout << "find success! " << endl << "-->key:" << iter->first << endl << "-->value:" << iter->second << endl; 
	}

	cout << endl; 

	
	
//6.删除
#if 0
	// 删除迭代器指向位置的键值对,并返回一个指向下一元素的迭代器
	iterator erase( iterator pos )
	 
	// 删除一定范围内的元素,并返回一个指向下一元素的迭代器
	iterator erase( const_iterator first, const_iterator last );
	 
	// 根据Key来进行删除, 返回删除的元素数量,在map里结果非0即1
	size_t erase( const key_type& key );
	 
	// 清空map,清空后的size为0
	void clear();
#endif


	int num_id_name = id_name.erase(28);
	for(iter = id_name.begin(); iter != id_name.end(); iter++)
	{
		cout << iter->first << ":" << iter->second << ";";
	}
	cout << endl;
	
	return 0;
}

 

 

附录:摘录及参考资料

  1. 《c++ primer plus》
  2. C++中STL用法超详细总结
  3. C语言中文网
  4. [C++ STL] 各容器简单介绍

 

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值