map类的介绍
map的声明如下,Key就是map底层关键字的类型,T是map底层value的类型,set默认要求Key支持小于比较,如果不支持或者需要的话可以自行实现仿函数传给第⼆个模版参数,map底层存储数据的内存是从空间配置器申请的。⼀般情况下,我们都不需要传后两个模版参数。map底层是用红黑树实现,增删查改效率是O(logN) ,迭代器遍历是走的中序,所以是按key有序顺序遍历的。
cpp
template < class Key, // map::key_type
class T, // map::mapped_type
class Compare = less<Key>, // map::key_compare
class Alloc = allocator<pair<const Key,T> > //
map::allocator_type
> class map;
pair类型介绍
map底层的红黑树节点中的数据,使用pair存储键值对数据。
pair是将2个数据组合成一组数据,当需要这样的需求时就可以使用pair,map就是将key和value放在一起来保存。
map的插入在成功时会返回pair<新插入值所在的迭代器,true>,插入失败会返回pair<已存在跟key等值的迭代器,false>
cpp
typedef pair<const Key, T> value_type;
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)
{}
template<class U, class V>
pair (const pair<U,V>& pr): first(pr.first), second(pr.second)
{}
};
template <class T1,class T2>
inline pair<T1,T2> make_pair (T1 x, T2 y)
{
return ( pair<T1,T2>(x,y) );
}
map的构造
map的迭代器支持正向和反向迭代遍历,遍历默认按key的升序顺序 ,因为底层是⼆叉搜索树,迭代器遍历走的中序;支持迭代器就意味着支持范围for,map支持修改value数据,不支持修改key数据,修改关键字数据,破坏了底层搜索树的结构。
cpp
// empty (1) ⽆参默认构造
explicit map (const key_compare& comp = key_compare(),
const allocator_type& alloc = allocator_type());
// range (2) 迭代器区间构造
template <class InputIterator>
map (InputIterator first, InputIterator last,
const key_compare& comp = key_compare(),
const allocator_type& = allocator_type());
// copy (3) 拷⻉构造
map (const map& x);
// initializer list (5) initializer 列表构造
map (initializer_list<value_type> il,
const key_compare& comp = key_compare(),
const allocator_type& alloc = allocator_type());
map的增删查
map增接口,插入的pair键值对数据,跟set所有不同,但是查和删的接口只用关键字key跟set是完全类似的,不过find返回iterator,不仅仅可以确认key在不在,还找到key映射的value,同时通过迭代还可以修改value。
map在插入的时候只会检查key,不会检查value,key相同则不会插入。
cpp
单个数据插⼊,如果已经key存在则插⼊失败,key存在相等value不相等也会插⼊失败
pair<iterator,bool> insert (const value_type& val);
列表插⼊,已经在容器中存在的值不会插⼊
void insert (initializer_list<value_type> il);
迭代器区间插⼊,已经在容器中存在的值不会插⼊
template <class InputIterator>
void insert (InputIterator first, InputIterator last);
查找k,返回k所在的迭代器,没有找到返回end()
iterator find (const key_type& k);
查找k,返回k的个数
size_type count (const key_type& k) const;
删除⼀个迭代器位置的值
iterator erase (const_iterator position);
删除k,k存在返回0,存在返回1
size_type erase (const key_type& k);
删除⼀段迭代器区间的值
iterator erase (const_iterator first, const_iterator last);
返回⼤于等k位置的迭代器
iterator lower_bound (const key_type& k);
返回⼤于k位置的迭代器
const_iterator lower_bound (const key_type& k) const;
map的数据修改
前⾯我提到map支持修改mapped_type数据,不支持修改key数据,修改关键字数据,破坏了底层搜索树的结构。
map第⼀个支持修改的方式时通过迭代器,迭代器遍历时或者find返回key所在的iterator修改,map 还有⼀个非常重要的修改接口operator[],但是operator[]不仅仅支持修改,还支持插⼊数据和查找数据,所以他是⼀个多功能复合接口,operator[]传key返回对应的value
需要注意从内部实现角度,map这里把我们传统说的value值,给的是T类型,typedef为 mapped_type。而value_type是红黑树结点中存储的pair键值对值。⽇常使⽤我们还是习惯将这⾥的 T映射值叫做value。
insert插入⼀个pair对象 :1.如果key已经在map中,插入失败,则返回⼀个pair对象,返回pair对象 first是key所在结点的迭代器,second是false 2.如果key不在在map中,插入成功,则返回⼀个pair对象,返回pair对象 first是新插⼊key所在结点的迭代器,second是true 也就是说无论插⼊成功还是失败,返回pair对象的first都会指向key所在的迭代器,那么也就意味着insert插⼊失败时充当了查找的功能,正是因为这⼀点,insert可以⽤来实现 operator[]
operator[]内部实现
cpp
mapped_type& operator[] (const key_type& k)
{
pair<iterator, bool> ret = insert({ k, mapped_type() });
iterator it = ret.first;
return it->second;
}
1、如果k不在map中 ,insert会插⼊k和mapped_type默认值,同时[]返回结点中存储
mapped_type值的引⽤,那么我们可以通过引用修改返映射值。所以[]具备了插⼊+修改功能
2、如果k在map中 ,insert会插⼊失败,但是insert返回pair对象的first是指向key结点的
迭代器,返回值同时[]返回结点中存储mapped_type值的引⽤,所以[]具备了查找+修改的功能
所以我们在使用operator[]需要注意该节点是否在map中存在,这影响了operator[]实现怎么样的操作。