map是STL中关联式容器的一种,所谓关联式容器,元素是按关键字来保存和访
问的,而序列式容器中的元素则是按它们在容器中的位置来顺序保存和访问的。
本片博客的主角map中,存储的不是一般的数据,而是一个个的键值对,我们称
之为pair,pair键值对由两部分组成:键值key和实值value。key与value之间具
有一一映射的关系。通常在查找过程中,我们通常都是通过key值来找value值。
这也满足了关联式容器的定义,通过key值关键字来保存和访问。
现实中有很多这种数据需要我们用map来存储,比如一个人与他的身份证号。
字典当中英文单词与中文含义等等。
map的底层是有由红黑树实现的(与set / multimap / multiset相同)。所以这里提几
个值得注意的点。
下面是map的STL源码结构:
template , class Alloc = alloc>
class set {
public:
// typedefs:
typedef Key key_type;//键值key
typedef Key value_type;//实值value(其实底层红黑树中key和value都是key值,
//只是取名不同)
typedef Compare key_compare;//比较器,默认传的是less,
//也可以显示的传函数指针或仿函数。
typedef Compare value_compare;
private:
typedef rb_tree, key_compare, Alloc> rep_type;
rep_type t; // red-black tree representing set
};
上面已经说过,map中存储的键值对叫pair,那么我们来看看源码中pair的结构
是什么样的:
template
struct pair
{
typedef _T1 first_type; /// @c first_type is the first bound type
typedef _T2 second_type; /// @c second_type is the second bound type
_T1 first; //注意,他是pulbic
_T2 second;
pair() : first(T1()) , second(T2()) //构造一个key和value都为空的pair
{}
pair(const T1& a,const T2& b): first(a),second(b)//构造一个键值为a,
//实值为b的pair
{}
}
可以看到,pair中存储的是一个个first和second就对应着我们这里说的key和value。
以下是map的迭代器的常用操作:
map的迭代器就是红黑树的迭代器,红黑树迭代器和list的迭代器有某些相同的
性质:当用户对map(list)进行了增加和删除操作后,所有的迭代器都依然有
效。
当然,被删除的结点的迭代器是个例外。
at
|
查找具有指定键值的元素。 |
begin | 返回一个迭代器,此迭代器指向映射中的第一个元素。 |
cbegin | 返回一个常量迭代器,此迭代器指向映射中的第一个元素。 |
cend | 返回一个超过末尾常量迭代器。 |
clear | 清除映射的所有元素。 |
count | 返回映射中其键与参数中指定的键匹配的元素数量。 |
crbegin | 返回一个常量迭代器,此迭代器指向反向映射中的第一个元素。 |
crend | 返回一个常量迭代器,此迭代器指向反向映射中最后一个元素之后的位置。 |
emplace | 将就地构造的元素插入到映射。 |
emplace_hint | 将就地构造的元素插入到映射,附带位置提示。 |
empty | 如果映射为空,则返回 true。 |
end | 返回超过末尾迭代器。 |
equal_range | 返回一对迭代器。 此迭代器对中的第一个迭代器指向 map 中其键大于指定键的第一个元素。 此迭代器对中的第二个迭代器指向 map 中其键等于或大于指定键的第一个元素。 |
erase | 从指定位置移除映射中的元素或元素范围。 |
find | 返回一个迭代器,此迭代器指向映射中其键与指定键相等的元素的位置。 |
get_allocator | 返回用于构造映射的 allocator 对象的副本。 |
insert | 将元素或元素范围插入到映射中的指定位置。 |
key_comp | 返回用于对映射中的键进行排序的比较对象副本。 |
lower_bound | 返回一个迭代器,此迭代器指向映射中其键值等于或大于指定键的键值的第一个元素。 |
max_size | 返回映射的最大长度。 |
rbegin | 返回一个迭代器,此迭代器指向反向映射中的第一个元素。 |
rend | 返回一个迭代器,此迭代器指向反向映射中最后一个元素之后的位置。 |
size | 返回映射中的元素数量。 |
swap | 交换两个映射的元素。 |
upper_bound | 返回一个迭代器,此迭代器指向映射中其键值大于指定键的键值的第一个元素。 |
value_comp | 检索用于对映射中的元素值进行排序的比较对象副本。 |
shrink_to_fit | 放弃额外容量。 |
size | 返回vector元素个数 |
swap | 交换两个向量的元素。 |
以上就是map的所有常见操作。这里唯独要对insert和operator[ ]进行进一步的说明。
先来看insert的原型:
//指定元素插入 返回值为pair 第二个参数bool值可以判断元素是否插入成功
pair insert (const value_type& val);
//指定位置插入,但位置可能是不合适的,会导致插入失败,插入成功返回新位置的迭代器
iterator insert (iterator position, const value_type& val);
//指定迭代器区间插入
template
void insert (InputIterator first, InputIterator last);
这里我们要说的是第一种插入方式,直接给一个val作为参数,插入则返回一个
pair,pair的第一个参数是迭代器,倘若本次插入成功,则返回新插入位置的迭
代器,倘若插入失败,那么就说明该值已经在map中,直接返回val所在位置的
迭代器,而第二个参数bool则说明是否插入成功,1标识插入成功,0标识插入
失败。
有了这个函数,我们就可以来讲一讲operator[ ]了,map中的operator[ ]让我们
能够像数组那样,通过key值(可以理解成数组里的下标值),找到value值(可
以理解成a[ i ])。
那operator[ ]的底层原理是:
再来看看底层operator[ ]的实现源码:
T& operator[](const key_type& k)
{
return (*((insert(value_type(k, T()))).first)).second;
}
一下子可能难以理解,我们拆分着看就很容易能理解它的思路了:
举例:
#include
#include
最后再说一点,operator[ ]一定会返回键值为key的键值对中value的引用,但是
他无法分清是原来就有还是新插入的,所以跟他类似的有一个at(),功能和他一
样,但是at()在key值不在的时候会抛异常。
#include
#include