STL源码之map与multimap
map
map的特性是,所有元素都会根据元素的键值自动被排序;
map的所有元素都是pair,同时拥有实值(value)和 键值 (key),pair的第一个元素视为键值,第二个元素视为实值;
map不允许两个元素具有相同的键值。
map的迭代器可以用来修改元素的实值(value),修改元素的实值并不会影响map的排列规则;
map的迭代器不可以用来改变map的键值,因为修改map的键值会严重破坏map的结构。
map对其中的元素进行删除或者新增操作,操作之前的迭代器,在操作完成之后依然有效,但是被操作的元素除外。
map的底层结构为rb-tree。
pair的定义
template<class _Ty1,
class _Ty2>
struct pair{ // store a pair of values
typedef pair<_Ty1, _Ty2> _Myt;
typedef _Ty1 first_type;
typedef _Ty2 second_type;
template<class _Uty1 = _Ty1,
class _Uty2 = _Ty2,
class = enable_if_t<is_default_constructible<_Uty1>::value
&& is_default_constructible<_Uty2>::value> >
constexpr pair()
: first(), second()
{ // default construct
}
_Ty1 first; // the first stored value
_Ty2 second; // the second stored value
}
map与multimap的区别
- map和multimap的key为key,value为key+data,与set是不同的,set的key就是value,value就是key。
- map的key不可修改,map与multimap的插入调用函数不同,影响了其key是否对应value。
- map有
[]
操作符,而multimap没有[]
操作符。
map的结构
map的红黑树的定义为:typedef _Rb_tree<key_type, value_type, _Select1st<value_type>,key_compare, _Pair_alloc_type> _Rep_type;可以看出 value_type是一个pair定义为 typedef std::pair<const _Key, _Tp> value_type; 其中 pair的第一个值为key,并且是const,第二个值为value。
_Select1st是一个仿函数,在stl_function
中可以找到源码如下:
template<typename _Pair>
struct _Select1st
: public unary_function<_Pair, typename _Pair::first_type>
{
typename _Pair::first_type&
operator()(_Pair& __x) const
{ return __x.first; }
};
template <typename _Key, typename _Tp, typename _Compare = std::less<_Key>,
typename _Alloc = std::allocator<std::pair<const _Key, _Tp> > >
class map
{
public:
typedef _Key key_type;
typedef _Tp mapped_type;
typedef std::pair<const _Key, _Tp> 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<value_type, value_type, bool>
{
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<value_type>::other _Pair_alloc_type;
typedef _Rb_tree<key_type, value_type, _Select1st<value_type>,
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() { }
...
}
map的insert操作
这里需要说明的是,insert的返回类型是一个pair,由一个迭代器和一个bool组成,后者bool的值表示当前是否插入成功;成功的话,迭代器指向插入的元素;如果失败则迭代器返回插入失败点的原始元素;将在【】操作符中利用该性质。
std::pair<iterator, bool>
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<typename _Key, typename _Val, typename _KeyOfValue,
typename _Compare, typename _Alloc>
pair<typename _Rb_tree<_Key, _Val, _KeyOfValue,
_Compare, _Alloc>::_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的[]操作符
map的【】操作符,是根据键值key和实值产生一个临时对象,在调用insert函数将这个临时对象插入到map中去,根据上述insert的描述,我们可以根据返回值的迭代器找到当前的key值对应的元素,由于返回值是引用,可对其元素进行操作。
mapped_type&
operator[](const key_type& __k)
{
// concept requirements
__glibcxx_function_requires(_DefaultConstructibleConcept<mapped_type>)
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<const key_type&>(__k),
std::tuple<>());
#else
__i = insert(__i, value_type(__k, mapped_type()));
#endif
return (*__i).second;
}
multimap
multimap的特性以及用法和map完全相同,区别在于:它允许键值重复,因此它的插入调用的是rb-tree的_M_insert_equal;
multimap并没有实现【】操作符。
iterator
insert(const value_type& __x)
{ return _M_t._M_insert_equal(__x); }
调用的是rb-tree内部的方法,实现如下:
template<typename _Key, typename _Val, typename _KeyOfValue,
typename _Compare, typename _Alloc>
#if __cplusplus >= 201103L
template<typename _Arg>
#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<typename _Key, typename _Val, typename _KeyOfValue,
typename _Compare, typename _Alloc>
pair<typename _Rb_tree<_Key, _Val, _KeyOfValue,
_Compare, _Alloc>::_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为插入点的父结点
}