STL关联式容器(1)——map和multimap

一、什么是关联式容器

STL中的部分容器,像 vector、list、deque、forward_list 等,这些容器统称为序列式容器,因为其底层为线性序列的数据结构,里面存储的是元素本身。那什么是关联式容器?它和序列式容器有什么区别?
关联式容器也是用来存储数据的,与序列式容器不同的是,其里面存储的是<key, value>结构的键值对,在数据检索时比序列式容器效率更高。

二、键值对

用来表示具有一一对应关系的一种结构,该结构中一般只包含两个成员变量 key 和 value,key 代表键,value 表示与 key 对应的信息。
STL 中关于键值对的定义:
 

template <class T1, class T2>
struct pair
{
 typedef T1 first_type;
 typedef T2 second_type;
 T1 first;
 T2 second;
 pair()
 : first(T1())
 , second(T2())
 {}
 
 pair(const T1& a, const T2& b)
 : first(a)
 , second(b)
{}
};

三、map

1.  在 map 中,键值 key 通常用于排序和惟一地标识元素,而值 value 中存储与此键值 key 关联的内容。键 key 和值 value 的类型可能不同,并且在 map 的内部,key 与 value 通过成员类型 value_type 绑定在一起,为其取别名称为 pair:
typedef pair value_type。
2. 在内部,map中的元素总是按照键值key进行比较排序的。(默认升序)
3. map 支持下标访问符,即在[]中放入 key ,就可以找到与 key 对应的 value 。
4. map 的底层是红黑树来实现的。

首先我们查看一下 map 的模板参数声明:

1. key: 键值对中key的类型
2. T: 键值对中value的类型
3. Compare: 比较器的类型,map中的元素是按照key来比较的,缺省情况下按照小于来比较,一般情况下(内置类型元素)该参数不需要传递,如果无法比较时(自定义类型),需要用户自己显式传递比较规则(一般情况下按照函数指针或者仿函数来传递)
4. Alloc:通过空间配置器来申请底层空间,不需要用户传递,除非用户不想使用标准库提供的空间配置器。

map 的迭代器和其他容器的迭代器用法一样,这里不做说明,我们可以利用迭代器去遍历 map:(该遍历函数后面会用到)
 

template<class K,class V>
void print(map<K,V>& m)
{
	for (auto it = m.begin(); it != m.end(); it++)
	{
		cout << (*it).first << "--->" << (*it).second << "  ";
	}
	cout << endl;
}

map的默认成员函数(构造、拷贝构造、赋值运算符重载):

函数声明功能
map (const key_compare& comp = key_compare(),const allocator_type& alloc = allocator_type());
构造一个空的map
 map (InputIterator first, InputIterator last,const key_compare& comp = key_compare(),const allocator_type& = allocator_type());
用[first,last)区间中元素来构造map
map (const map& x)拷贝构造
map& operator= (const map& x)
赋值运算符重载

除了上述几种构造函数之外,map 还支持(c++11)使用花括号的方式来初始化构造。
 

void testmap1()
{
	map<int, string> m1;
	map<int, string> m2{
		{ 4, "four" }, { 2, "two" }, { 3, "three" }, 
		{ 1, "one" },{ 5, "five" },{ 2,“two” }
	};
	map<int, string> m3(m2);
	map<int, string> m4(m2.begin(), m2.end());
	m1 = m2;
	print(m1);
	print(m2);
	print(m3);
	print(m4);

}

程序运行结果如下:

我们可以看到通过上述几种方式都可以初始化构造或者赋值 map。可以看到 map 是有序的,默认升序,而且键 key 唯一,没有重复。

operator[]

operator[] 的原理:
用<key, T()>构造一个键值对,然后调用 insert() 函数将该键值对插入到 map 中。如果 key 已经存在,插入失败,insert 函数返回该 key 所在位置的迭代器,如果 key 不存在,插入成功,insert 函数返回新插入元素所在位置的迭代器。
operator[] 函数最后将 insert 返回值键值对中的 value 返回。
例如:
1. 假设现在有一个 map<int,string> m; 我们执行语句 m[2]; 即调用 insert() 将 <2,""> 插入到 map 中。
2. 假设现在有一个 map<int,string> m; 我们执行语句 m[2]="two"; 即调用 insert() 将 <2,"">插入到 map 中,然后 operator[] 返回 value的引用,再将 "two" 赋值给 value 的引用。如果,我们此时执行语句 cout<<m[2]<<endl; 首先调用 insert(),插入失败,返回 value值 "two"。

在元素访问时,有一个与 operator[] 类似的操作 at() (该函数不常用)函数,都是通过 key 找到与 key 对应的 value 然后返回其引用,不同的是:当 key 不存在时,operator[] 用默认 value 与 key 构造键值对,然后插入,返回该默认 value,at() 函数直接抛异常。

void testmap2()
{
	map<int, string> m1{
		{ 4, "four" }, { 2, "two" }, { 3, "three" },
		{ 1, "one" }, { 5, "five" }
	};
	m1[6];
	m1[7] = "seven";
	//m1.at(8); 程序抛异常 直接崩掉
	print(m1);
}

程序运行结果: 

map 中的增删查改

函数声明功能
pair<iterator,bool> insert (const value_type& val);
插入键值对 val。返回值也是键值对:iterator代表插入键值对的迭代器,bool代表是否插入成功
template <class InputIterator>
void insert (InputIterator first, InputIterator last)
在map中插入[first, last)区间中的元素
iterator insert(const_iterator position, const value_type& val)
在position位置插入值为val的键值对,返回该键值对在map中的位置,注意:元素不一定必须插在position位置,该位置只是一个参考。
void erase ( iterator position ) 删除position位置的元素
size_type erase ( const key_type& x )删除键为 x的元素
void erase ( iterator first, iterator last )删除[first, last)区间中的元素
void swap( map<Key,T,Compare,Allocator>& 
mp )
交换两个map
size_type count ( const key_type& x ) const返回 key 值为x的元素个数,因此该函数的返回值要么为0,要么为1,因此也可以用该函数来检测一个key是否在map中
void clear ( ) 将map 中元素清空
iterator find ( const key_type& x )找到返回该元素的位置的迭代器,否则返回end
void testmap3()
{
	map<int, string> m1;
	map<int, string> m2{
		{ 1, "one" }, { 4, "four" }
	};

	m1.insert(pair<int, string>(3, "three"));
	m1.insert(make_pair(2, "two"));
	m1.insert(m1.find(1), make_pair(5, "five"));
	m1.find(6);
	m2.insert(m1.begin(), m1.end());
	print(m1);
	print(m2);


	m1.erase(5);
	m1.erase(m1.find(2));
	print(m1);

	m1.swap(m2);
	print(m1);
	print(m2);
	
}

程序运行结果:


四、multimap

multimap 的使用和 map基本一样,唯一不同的地方是 multimap 中key可以重复,我们来看一段代码简单了解即可:
 

void testmultimap()
{
	multimap<int, string> m1{
		{ 4, "four" }, { 2, "two" }, { 3, "three" },
		{ 2, "two" }, { 5, "five" }, { 5, "five" }, { 1, "one" }
	};
	multimap<int, string>::iterator it = m1.begin();
	while (it != m1.end())
	{
		cout << (*it).first << "-->" << (*it).second << "  ";
		it++;
	}
	cout << endl;
}

程序运行结果:

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值