map的特性是,所有元素都会根据元素的键值自动被排序;
map的所有元素都是pair,同时拥有实值(value)和 键值 (key),pair的第一个元素视为键值,第二个元素视为实值;
map不允许两个元素具有相同的键值。
map的迭代器可以用来修改元素的实值(value),修改元素的实值并不会影响map的排列规则;
map的迭代器不可以用来改变map的键值,因为修改map的键值会严重破坏map的结构。
map对其中的元素进行删除或者新增操作,操作之前的迭代器,在操作完成之后依然有效,但是被操作的元素除外。
map的底层结构为rb-tree。
template
struct pair{ // store a pair of values
typedef pair<_Ty1, _Ty2> _Myt;
typedef _Ty1 first_type;
typedef _Ty2 second_type;
template::value
&& is_default_constructible<_Uty2>::value> >
constexpr pair()
: first(), second()
{ // default construct
}
_Ty1 first; // the first stored value
_Ty2 second; // the second stored value
}
[]
操作符,而multimap没有[]
操作符。 map的红黑树的定义为:typedef _Rb_tree
_Select1st是一个仿函数,在stl_function
中可以找到源码如下:
template
struct _Select1st
: public unary_function<_Pair, typename _Pair::first_type>
{
typename _Pair::first_type&
operator()(_Pair& __x) const
{ return __x.first; }
};
template ,
typename _Alloc = std::allocator > >
class map
{
public:
typedef _Key key_type;
typedef _Tp mapped_type;
typedef std::pair value_type;
typedef _Compare key_compare;
typedef _Alloc allocator_type;
private:
// concept requirements
typedef typename _Alloc::value_type _Alloc_value_type;
__glibcxx_class_requires(_Tp, _SGIAssignableConcept)
__glibcxx_class_requires4(_Compare, bool, _Key, _Key,
_BinaryFunctionConcept)
__glibcxx_class_requires2(value_type, _Alloc_value_type, _SameTypeConcept)
public:
class value_compare
: public std::binary_function
{
friend class map<_Key, _Tp, _Compare, _Alloc>;
protected:
_Compare comp;
value_compare(_Compare __c)
: comp(__c) { }
public:
bool operator()(const value_type& __x, const value_type& __y) const
{ return comp(__x.first, __y.first); }
};
private:
/// This turns a red-black tree into a [multi]map.
typedef typename __gnu_cxx::__alloc_traits<_Alloc>::template
rebind::other _Pair_alloc_type;
typedef _Rb_tree,
key_compare, _Pair_alloc_type> _Rep_type;
/// The actual tree structure.
_Rep_type _M_t;
typedef __gnu_cxx::__alloc_traits<_Pair_alloc_type> _Alloc_traits;
public:
// many of these are specified differently in ISO, but the following are
// "functionally equivalent"
typedef typename _Alloc_traits::pointer pointer;
typedef typename _Alloc_traits::const_pointer const_pointer;
typedef typename _Alloc_traits::reference reference;
typedef typename _Alloc_traits::const_reference const_reference;
typedef typename _Rep_type::iterator iterator;
typedef typename _Rep_type::const_iterator const_iterator;
typedef typename _Rep_type::size_type size_type;
typedef typename _Rep_type::difference_type difference_type;
typedef typename _Rep_type::reverse_iterator reverse_iterator;
typedef typename _Rep_type::const_reverse_iterator const_reverse_iterator;
// [23.3.1.1] construct/copy/destroy
// (get_allocator() is also listed in this section)
/**
* @brief Default constructor creates no elements.
*/
map()
: _M_t() { }
...
}
这里需要说明的是,insert的返回类型是一个pair,由一个迭代器和一个bool组成,后者bool的值表示当前是否插入成功;成功的话,迭代器指向插入的元素;如果失败则迭代器返回插入失败点的原始元素;将在【】操作符中利用该性质。
std::pair
insert(const value_type& __x)
{ return _M_t._M_insert_unique(__x); }
map的insert操作调用的也是Rb-tree的_M_insert_unique方法,在这个方法中 最终调用的是_M_get_insert_unique_pos实现了:插入新值,并判断是否重复,若重复插入无效。
template
pair::_Base_ptr,
typename _Rb_tree<_Key, _Val, _KeyOfValue,
_Compare, _Alloc>::_Base_ptr>
_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::
_M_get_insert_unique_pos(const key_type& __k)
{
typedef pair<_Base_ptr, _Base_ptr> _Res;
_Link_type __x = _M_begin();
_Link_type __y = _M_end();
bool __comp = true;
while (__x != 0)//从根节点开始,寻找合适的插入点
{
__y = __x;
__comp = _M_impl._M_key_compare(__k, _S_key(__x));
__x = __comp ? _S_left(__x) : _S_right(__x);
}
iterator __j = iterator(__y);//指向插入点的父结点
if (__comp)//comp为true,说明插在左侧
{
if (__j == begin())//插入结点的父结点为最左侧结点
return _Res(__x, __y);
else
--__j;
}
//新键值不与既有结点重复,于是执行安插
if (_M_impl._M_key_compare(_S_key(__j._M_node), __k))
return _Res(__x, __y);
return _Res(__j._M_node, 0);
}
map的【】操作符,是根据键值key和实值产生一个临时对象,在调用insert函数将这个临时对象插入到map中去,根据上述insert的描述,我们可以根据返回值的迭代器找到当前的key值对应的元素,由于返回值是引用,可对其元素进行操作。
mapped_type&
operator[](const key_type& __k)
{
// concept requirements
__glibcxx_function_requires(_DefaultConstructibleConcept)
iterator __i = lower_bound(__k);
// __i->first is greater than or equivalent to __k.
if (__i == end() || key_comp()(__k, (*__i).first))
#if __cplusplus >= 201103L
__i = _M_t._M_emplace_hint_unique(__i, std::piecewise_construct,
std::tuple(__k),
std::tuple<>());
#else
__i = insert(__i, value_type(__k, mapped_type()));
#endif
return (*__i).second;
}
multimap的特性以及用法和map完全相同,区别在于:它允许键值重复,因此它的插入调用的是rb-tree的_M_insert_equal;
multimap并没有实现【】操作符。
iterator
insert(const value_type& __x)
{ return _M_t._M_insert_equal(__x); }
调用的是rb-tree内部的方法,实现如下:
template
#if __cplusplus >= 201103L
template
#endif
typename _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::iterator
_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::
#if __cplusplus >= 201103L
_M_insert_equal(_Arg&& __v)
#else
_M_insert_equal(const _Val& __v)
#endif
{
pair<_Base_ptr, _Base_ptr> __res
= _M_get_insert_equal_pos(_KeyOfValue()(__v));
return _M_insert_(__res.first, __res.second, _GLIBCXX_FORWARD(_Arg, __v));
}
上述代码中调用了_M_get_insert_equal_pos,其中的实现是从根节点开始,往下寻找适当的插入点,区别于上述的新值的判断,这里是不做判断是否有重复的。
template
pair::_Base_ptr,
typename _Rb_tree<_Key, _Val, _KeyOfValue,
_Compare, _Alloc>::_Base_ptr>
_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::
_M_get_insert_equal_pos(const key_type& __k)
{
typedef pair<_Base_ptr, _Base_ptr> _Res;
_Link_type __x = _M_begin();
_Link_type __y = _M_end();
while (__x != 0)//寻找合适的插入点
{
__y = __x;
__x = _M_impl._M_key_compare(__k, _S_key(__x)) ?
_S_left(__x) : _S_right(__x);//遇大往左,遇小往右
}
return _Res(__x, __y);//x为插入点,y为插入点的父结点
}