为了防止学习的过程中出现学后忘的情况,我将接触到的知识进行整理,作该笔记,由于本人水平有限,如果出现错误,还望赐正。
(from The C++ Standard Library (Second Edition))
关联式容器依据特定的排序准则,自动为其元素排序。元素可以是任何类型的 value,也可以是 key/value pair,其中 key 可以是任何类型,映射至一个相关 value,而 value 也可以是任意类型。排序准则以函数形式呈现,用来比较 value,或比较 key/value 中的 key 。默认情况下所以容器都以操作符 < 进行比较,但是你可以提供自己的比较函数,定义不同的排序准则。
通常关联式容器由二叉树实现出来。在二叉树中,每个元素(节点)都有一个父节点和两个子节点,左子树的所有元素都比自己小,右子树的所有元素都比自己大。关联容器的差别主要在于元素的种类以及处理重复元素时的方式。
关联式容器的主要优点是,它能很快找出一个具有某特定 value 的元素,因为它具备对数复杂度,而任何循序式容器的复杂度是线性。因此,使用关联式容器,面对1000个元素,平均而言你将有10次而不是500次比较动作。然而它有一个缺点是,你不能直接改动元素的 value ,因为那会破坏元素的自动排序。
下面是 STL 定义的关联式容器:
所有关联式容器都有一个可供选择的 template 实参,指明排序准则,默认采用操作符 <。排序准则也被用来测试等同性:如果两个元素的 value/key 互不小于对方,则两者被视为重复。
成员类型 | 定义 |
---|---|
key_type | 第一个参数模板 Key |
mapped_type | 第二个参数模板 Tp |
value_type | std::pair |
key_compare | 第三个参数模板 Compare |
value_compare | 嵌套函数类以比较元素 |
allocator_type | 第四个参数模板 Alloc |
reference | value_type& |
const_reference | const value_type& |
pointer | allocator_traits |
const_pointer | allocator_traits |
iterator | value_type的双向迭代器 |
const_iterator | const value_type的双向迭代器 |
reverse_iterator | reverse_iterator |
const_reverse_iterator | reverse_iterator |
difference_type | 一个有符号整数类型 |
size_type | 无符号整数类型 |
成员类型定义如下:
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;
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;
};
函数名称 | 功能 |
---|---|
(constructor) | 构造函数 |
(destructor) | 析构函数 |
operator= | 复制一个map内容到另一个map中 |
begin( ),end(),cbegin(),cend() | 正向迭代器 |
rbegin(),rend(),crbegin(),crend() | 反向迭代器 |
==、!=、>、<、>=、<= | 比较 |
empty() | 判断容器是否为空 |
size() | 返回容器大小 |
max_size() | 返回最大容器大小 |
operator[]、at() | 获取元素 |
insert() | 插入元素 |
erase() | 删除元素 |
swap() | 交换两个map容器的内容 |
clear() | 清空容器 |
emplace() | 构造和插入元素 |
emplace_hint() | 按提示构造和插入元素 |
key_comp() | 返回键比较对象 |
value_comp() | 返回值比较对象 |
find() | 查找元素 |
count() | 用特定的键计数元素 |
lower_bound() | 将迭代器返回到下界 |
upper_bound() | 将迭代器返回到上界 |
equal_range() | 得到相等元素的值域 |
get_allocator() | 返回分配器 |
explicit map (const key_compare& comp = key_compare(), const allocator_type& alloc = allocator_type());
//定义一个默认状态的空map容器
explicit map(const key_compare& comp, const allocator_type& a = allocator_type());
//自定义比较方法
explicit map (const key_compare& comp = key_compare(), const allocator_type& alloc);
//自定义分配器
templatemap (InputIterator first, InputIterator last, const key_compare& comp = key_compare(), const allocator_type& = allocator_type());
//使用迭代器初始化map容器
map (const map& x);
//使用一个map容器初始化该容器
map (const map& x, const allocator_type& alloc);
//同上,可自定义分配器
map (initializer_listil, const key_compare& comp = key_compare(), const allocator_type& alloc = allocator_type());
//使用初值列初始化map容器
#include
#include
#include
using namespace std;
int main()
{
map<string,int> arr1;//定义一个空的map容器
map<string,int> arr2{
{"小明",10086},
{"小红",1008611},
{"小兰",123456}
};//使用初值列定义一个包含三个string/int的map容器
for(auto i:arr2) //输出所有元素
cout<<i.first<<" "<<i.second<<endl;
cout<<endl;
map<string,int> arr3(arr2);//使用一个map容器作为初始值
for(auto i:arr3) //输出所有元素
cout<<i.first<<" "<<i.second<<endl;
cout<<endl;
map<string,int> arr4(++arr2.begin(),arr2.end());//使用迭代器赋值
for(auto i:arr4) //输出所有元素
cout<<i.first<<" "<<i.second<<endl;
}
运行结果:
小兰 123456
小明 10086
小红 1008611
小兰 123456
小明 10086
小红 1008611
小明 10086
小红 1008611
第三个模板需传入函数指针或者函数对象。
#include
#include
#include
using namespace std;
bool upper(int a,int b){return a>b;} //自定义大到小比较函数
bool lower(int a,int b){return a<b;} //自定义小到大比较函数
int main()
{
map<int,string,bool(*)(int,int)> arr({{1,"小红"},{3,"小明"},{2,"小兰"}},lower);//按照key从小到大排列
for(auto i:arr)
cout<<i.first<<" "<<i.second<<endl;
cout<<endl;
map<int,string,bool(*)(int,int)> buf({{1,"小红"},{3,"小明"},{2,"小兰"}},upper);//按照key从大到小排列
for(auto i:buf)
cout<<i.first<<" "<<i.second<<endl;
}
程序运行结果为:
1 小红
2 小兰
3 小明
3 小明
2 小兰
1 小红
如果 map 或 multimap 中的键是指针的话,那么需要定义一个函数来比较它们所指向的对象,否则会比较指针所表示的地址,这并不是我们想要的。如果键是不支持直接进行 < 或 > 比较的类型,为了可以在 map 或 multimap 中使用它们,必须为它们定义一个适当的函数对象。处理这两种情况的方式在本质上相同。
#include
#include
#include
#include
#include
using namespace std;
class Key_compare
{
public:
bool operator () (const unique_ptr<string>& p1, const unique_ptr <string>& p2) const
{
return *p1 < *p2;
}
};
int main()
{
map<unique_ptr<string>,string,Key_compare> phonebook;
phonebook.emplace(make_unique<string>("Fred"), "914-626-7897");
phonebook.insert(std::make_pair(make_unique<string>("Lily"), "212-896-4337"));
phonebook.emplace(make_unique<string>("Eloik"), "871-343-4714");
for (const auto& p: phonebook)
cout << *p.first << " " << p.second << endl;
}
程序运行结果为:
Eloik 871-343-4714
Fred 914-626-7897
Lily 212-896-4337
map&operator =(const map&x);
map&operator =(map && x);
map&operator =(initializer_listil);
操作符= 功能是将值赋给map对象,值可以为一个map容器,也可以是一个初值列。
void swap (map& x);
swap 功能为交换两个map容器。
无论是= 还是 swap,操作两个不同的容器时,其key/value 必须一致。
#include
#include
#include
using namespace std;
bool upper(string a,string b){return a>b;}
int main()
{
map<string,int> arr1;
map<string,int> arr2;
map<string,int> arr3;
map<string,int,bool(*)(string,string)> arr4(upper);
map<string,char> arr5;
arr1 = { {"小明",10086},
{"小红",1008611},
{"小兰",123456}}; //使用初值列赋值
arr2 = arr1; //使用容器赋值
arr3["小黑"] = 666666; //使用数组的方法赋值
arr1.swap(arr3); //交换两个容器
//arr2 = arr4;//错误 参数模板 不一致
//arr2 = arr5;//错误 key/value 不一致
//arr2.swap(arr4); //错误 参数模板 不一致
//arr2.swap(arr5); //错误 key/value 不一致
}
insert() 函数原型:
pair
insert (const value_type& val);
//插入单个 key/value
templatepair insert (P&& val);
//返回插入情况,如果插入成功第二个模板为true,如果有重复则为false
iterator insert (const_iterator position, const value_type& val);
//带提示位置的插入
templateiterator insert (const_iterator position, P&& val);
//带提示位置的插入,如果重复则返回已经存在的值的迭代器,否则返回插入的值的迭代器
templatevoid insert (InputIterator first, InputIterator last);
//使用迭代器插入其他容器的值
void insert (initializer_listil);
//使用初值列插入值
下面用一个例子介绍所有插入方法(所有插入均会自动按准则排序):
#include
#include
#include
#include
using namespace std;
int main()
{
map<string,int> arr;
map<string,int> buf{{"红",255},{"橙",254},{"黄",253}};
arr.insert(make_pair("小明",123456));//插入单个值
arr.insert({"小红",123}); //用初值列插入单个值
arr.insert({make_pair("小兰",1234),
make_pair("小黑",9876) }); //使用初值列插入多个值
/*等价于 arr.insert({ {"小兰",1234},
{"小黑",9876} }); */
arr.insert(buf.begin(),buf.end()); //使用迭代器插入其他map容器的值
pair<map<string,int>::iterator,bool> ret;
ret = arr.insert(pair<string,int>("小红",123456));
if(ret.second == false){
cout<<"This value already exists,insert error---";
cout<<" key/value: "<<ret.first->first<<" "<<ret.first->second<<endl;
} //检查插入的值是否已经存在,注意返回的是已经存在的值
map<string,int>::iterator it = arr.begin(); //带提示位置的插入,正确的提示可以让插入更高效
arr.insert(it,make_pair("小白",333));
map<string,int>::iterator iter;
iter = arr.insert(it,make_pair("小红",333));//带提示位置的插入,如果重复则返回已经存在的值的迭代器,否则返回插入的值的迭代器
cout<<iter->first <<" "<<iter->second <<endl;
map<string,int> mymap;
mymap.insert(arr.begin(),arr.find("小白")); //范围内插入
cout<<endl;
for(auto&i:arr) //输出arr容器的值
cout<<i.first<<" "<<i.second<<endl;
cout<<endl;
for(auto&i:mymap) //输出mymap容器的值
cout<<i.first<<" "<<i.second<<endl;
}
输出结果为:
This value already exists,insert error— key/value: 小红 123
小红 123
小兰 1234
小明 123456
小白 333
小红 123
小黑 9876
橙 254
红 255
黄 253
小兰 1234
小明 123456
template
pair emplace (Args&&… args);
template
iterator emplace_hint (const_iterator position, Args&&… args);
如果一个新元素的键是唯一的,则插入该元素,这个新元素是使用args作为构造value_type(它是pair类型的对象)的参数来构造的。如果插入,这将有效地将容器大小增加1。
下面一个例子使用 emplace() 和 emplace_hint() :
#include
#include
#include
#include
#include
using namespace std;
struct rgb{
int r;
int g;
int b;
string color;
}RGB1,RGB2;
int main()
{
map<int,rgb> rainbow;
rainbow = {
{1,{255,0,0,"红"}},
{3,{255,255,0,"黄"}},
{4,{0,255,0,"绿"}},
{6,{0,0,255,"蓝"}},
{7,{255,0,255,"紫"}}
};
RGB1 = {255,125,0,"橙"};
rainbow.emplace(2,RGB1); //安放一个值
//相当于 rainbow.insert(make_pair(2,RGB1));
RGB2 = {0,255,255,"青"};
map<int,rgb>::iterator it = rainbow.begin();
advance(it,4); //将迭代器 it 向后移动四下
rainbow.emplace_hint(it,5,RGB2); //带提示的安放,提示恰当可以提高效率
//相当于 rainbow.insert(it,make_pair(5,RGB2));
for(auto&i:rainbow)
cout<<i.first<<" "<<i.second.color<<":"<<
i.second.r<<","<<i.second.g<<","<<i.second.b<<endl;
}
程序运行结果为:
1 红:255,0,0
2 橙:255,125,0
3 黄:255,255,0
4 绿:0,255,0
5 青:0,255,255
6 蓝:0,0,255
7 紫:255,0,255
operator[] 函数原型:
mapped_type& operator[] (const key_type& k);
mapped_type& operator[] (key_type&& k);
at() 函数原型:
mapped_type& at (const key_type& k);
const mapped_type& at (const key_type& k) const;
at() 访问元素时会进行检查,如果容器内没有这个键,则抛出 out_of_range 异常,而operator[] 不会检查。
size_type size() const;
size_type max_size() const;
bool empty() const;
iterator find (const key_type& k);
const_iterator find (const key_type& k) const;
iterator lower_bound (const key_type& k);
const_iterator lower_bound (const key_type& k) const;
iterator upper_bound (const key_type& k);
const_iterator upper_bound (const key_type& k) const;
pair
equal_range (const key_type& k) const;
pairequal_range (const key_type& k);
因为 map 不允许出现重复键,所以该函数返回的范围至多包含一个元素。如果没有找到匹配项,则返回的范围为0,两个迭代器都指向 键k之后第一个或与之等效键的元素。
size_type count (const key_type& k) const;
因为map容器中的所有元素都是唯一的,所以函数只能返回1(如果找到元素)或0(未找到元素)。
下面以一个例子示范上述函数的用法:
#include
#include
#include
#include
#include
using namespace std;
int main()
{
map<char,int> mymap;
mymap['a'] = 10;
mymap['b'] = 20;
mymap['d'] = 30;
mymap['e'] = 40;
mymap['f'] = 50;
mymap.at('a') = 60; //可以,将 a 对应的值更换为 60
//mymap.at('q') = 60; //错误! 键 q 不存在,抛出 out_of_range 异常
cout<<mymap['a']<<" "<<mymap.at('b')<<" "
<<mymap['d']<<" "<<mymap.at('e')<<" "<<mymap['f']<<endl;
map<char,int>::iterator it;
it = mymap.find('g'); //查找键为 g 的元素
if(it != mymap.end())
cout<<it->first<<" "<<it->second<<endl;
else
cout<<"The key was not found!"<<endl;
it = mymap.find('a'); //查找键为 a 的元素
if(it != mymap.end())
cout<<it->first<<" "<<it->second<<endl;
else
cout<<"The key was not found!"<<endl;
it = mymap.lower_bound('c'); //返回在键c之后第一个或与之等效键的元素的迭代器
cout<<it->first<<" "<<it->second<<endl;
it = mymap.upper_bound('d');//返回在键d之后的第一个键的元素的迭代器
cout<<it->first<<" "<<it->second<<endl;
pair<map<char,int>::iterator,map<char,int>::iterator> iters;
iters = mymap.equal_range('d'); //查找与键d 相等的键
cout<<iters.first->first<<" "<<iters.first->second<<endl; //指向键d
cout<<iters.second->first<<" "<<iters.second->second<<endl;//指向键d之后的一个键
cout<<"mymap's size:"<<mymap.size()<<endl;//输出容器大小
cout<<"mymap's max_size could be:"<<mymap.max_size()<<endl; //输出容器最大可能大小
for(char c = 'a';c<'h';++c){
if(mymap.count(c) > 0)
cout<<c<<" is an element of mymap"<<endl;
else
cout<<c<<" is not an element of mymap!"<<endl;
}
}
程序运行结果为:
60 20 30 40 50
The key was not found!
a 60
d 30
e 40
d 30
e 40
mymap’s size:5
mymap’s max_size could be:461168601842738790
a is an element of mymap
b is an element of mymap
c is not an element of mymap!
d is an element of mymap
e is an element of mymap
f is an element of mymap
g is not an element of mymap!
由于map会自动按照准则排序,所以其元素的key不能改变,而value可以改变。如果必须要改变key,可以删除这个元素,然后新插入一个。
iterator erase (const_iterator position);
iterator erase (iterator position);
//删除迭代器指向的元素
size_type erase (const key_type& k);
//删除拥有键 k的元素
iterator erase (const_iterator first, const_iterator last);
//删除迭代器指定的范围内的元素 [first,last)
void clear() noexcept;
一个小例子:
#include
#include
#include
#include
#include
using namespace std;
int main()
{
map<char,int> mymap;
mymap['a'] = 10;
mymap['b'] = 20;
mymap['d'] = 30;
mymap['e'] = 40;
mymap['f'] = 50;
map<char,int> buf(mymap);
mymap.erase(mymap.find('a')); //删除键为a的元素
mymap.erase('d');//删除键为d的元素
map<char,int>::iterator it = buf.begin();
advance(it,3);
buf.erase(it,buf.end()); //删除区间 [三,末尾] 的元素
for(auto&i:mymap)
cout<<i.first<<" "<<i.second<<endl;
cout<<endl;
for(auto&i:buf)
cout<<i.first<<" "<<i.second<<endl;
mymap.clear(); //清空mymap元素
cout<<mymap.size()<<endl;
}
运行结果为:
b 20
e 40
f 50
a 10
b 20
d 30
0
map对象的比较对象在构造时设置。它的类型(成员key_compare)是模板的第三个模板参数。默认情况下,这是一个less对象,它返回与操作符<相同的值。
这个对象决定容器中元素的顺序:它是一个函数指针或一个函数对象,它接受两个与元素键类型相同的参数,如果第一个参数按照它定义的严格的弱序排在第二个之前,则返回true,否则返回false
key_compare key_comp() const;
内部实现类似于:
template <class Key, class T, class Compare, class Alloc>
class map<Key,T,Compare,Alloc>::value_compare
{ // in C++98, it is required to inherit binary_function
friend class map;
protected:
Compare comp;
value_compare (Compare c) : comp(c) {} // constructed with map's comparison object
public:
typedef bool result_type;
typedef value_type first_argument_type;
typedef value_type second_argument_type;
bool operator() (const value_type& x, const value_type& y) const
{
return comp(x.first, y.first);
}
}
value_compare value_comp() const;
下面一个例子使用以上两个函数:
#include
#include
#include
#include
#include
using namespace std;
int main()
{
map<char,int> mymap;
map<char,int>::key_compare mycomp = mymap.key_comp();
for(char i='a';i<='z';++i){
mymap.emplace(i,static_cast<int>(i));
}
cout << "mymap'elements whose key no more than 'e' contains:\n";
char highest = 'e';
map<char,int>::iterator it = mymap.begin();
do {
cout << it->first << " => " << it->second << endl;
} while ( mycomp((*it++).first, highest) );//输出key不大于 'e'的元素
cout << endl;
cout << "mymap'elements whose key no more than 'c' contains:\n";
pair<char,int> high = {'c',101};
std::map<char,int>::iterator iter = mymap.begin();
do {
std::cout << iter->first << " => " << iter->second << endl;
} while ( mymap.value_comp()(*iter++, high) );//输出key不大于 'c'的元素
return 0;
}
输出:
mymap’elements whose key no more than ‘e’ contains:
a => 97
b => 98
c => 99
d => 100
e => 101
mymap’elements whose key no more than ‘c’ contains:
a => 97
b => 98
c => 99
(1)
template
bool operator== ( const map& lhs, const map & rhs );
(2)
template
bool operator!= ( const map& lhs, const map & rhs );
(3)
template
bool operator< ( const map& lhs, const map & rhs );
(4)
template
bool operator<= ( const map& lhs, const map & rhs );
(5)
template
bool operator> ( const map& lhs, const map & rhs );
(6)
template
bool operator>= ( const map& lhs, const map & rhs );
#include
#include
using namespace std;
int main()
{
map<char,int> foo,bar;
foo['a']=10;
foo['b']=200;
bar['a']=11;
bar['z']=1000;
if(foo>bar)
cout<<"abc"<<endl;
else if(foo<bar)
cout<<"xyz"<<endl; //输出 xyz
}
比较规则:先比较键,如果有差异直接返回,否则比较该键对应的值,如果有差异,直接返回,否则再比较下一个键。
multimap 容器保存的是有序的键/值对,但它可以保存重复的元素(重复键的排序方式为:不改变其相对顺序)。multimap 中会出现具有相同键的元素序列,它们会被添加到容器中。multimap 和 map 有相同范围的构造函数,默认的比较键的函数是 less()。
multimap 大部分成员函数的使用方式和 map 相同。因为重复键的原因,multimap 有一些函数的使用方式和 map 有一些区别。接下来介绍 multimap 和 map 容器不同的那些成员函数的用法。
multimap 不支持下标运算符,因为键并不能确定一个唯一元素。同样,multimap 也不能使用 at() 函数。multimap 的成员函数 find() 可以返回一个键和参数匹配的元素的迭代器。
如果使用 multimap 容器,几乎可以肯定它会包含键重复的元素;否则,就应该使用 map。一般来说,我们想访问给定键对应的所有元素。成员函数 equal_range() 就可以做到这一点。它会返回一个封装了两个迭代器的 pair 对象,这两个迭代器所确定范围内的元素的键和参数值相等。
pair
equal_range (const key_type& k) const;
pairequal_range (const key_type& k);
multimap 获取相同键元素 :
#include
#include
#include
#include
#include
using namespace std;
int main()
{
multimap<string, size_t> people {{"Ann",44},{"Ann",25},{"Bill", 46}, {"Jack", 77},
{"Jack", 32},{"Jill", 32}, {"Ann", 35}};
auto it = people.find("Ann");//输出指向第一个Ann的迭代器
if(it != people.end())
cout<<it->first<<" "<<it->second<<endl;
cout<<endl;
auto pr = people.equal_range("Ann");//遍历所有键为 Ann 的元素
if(pr.first != end(people))
{
for (auto iter = pr.first ; iter != pr.second; ++iter)
cout << iter->first << " is " << iter->second << endl;
}
}
通过调试窗口可以看到,相同键的元素并不会改变构造时的相对顺序。
程序输出结果如下:
Ann 44
Ann is 44
Ann is 25
Ann is 35
equal_range() 的参数可以是和键同类型的对象,或是不同类型的但可以和键比较的对象。返回的 pair 对象的成员变量 first 是一个迭代器,它指向第一个大于等于参数的元素;如果键和参数相等的元素存在的话,它是第一个键和参数相同的元素。如果键不存在,pair 的成员变量 first 就是容器的结束迭代器,所以应该总是对它们进行捡查。
pair 的成员变量 second 也是一个迭代器,它指向键值大于参数的第一个参数;如果没有这样的元素,它会是一个结束迭代器。
multimap 的成员函数 lower_bound() 会返回一个迭代器,它指向键值和参数相等或大于参数的第一个元素,或者指向结束迭代器。upper_bound() 也返回一个迭代器,它指向键值大于函数参数的第一个元素,如果这样的元素不出现的话,它就是一个结束迭代器。所以,当存在一个或多个相等键时,这些函数会返回一个开始迭代器和一个结束迭代器,它们指定了和参数匹配的元素的范围,这和 equal_range() 返回的迭代器是相同的。因而前面的代码段可以这样重写:(仍然是遍历所有键为 Ann 的元素)
auto iter1 = people.lower_bound("Ann");
auto iter2 = people.upper_bound("Ann");
if(iter1 != end(people))
{
for(auto iter = iter1 ; iter != iter2; ++iter)
cout << iter->first << " is " << iter->second << endl;
}
注意,全局的 equal_range()、lower_bound()、upper_bound() 函数模板的使用方式和关联容器中同名成员函数的使用方式略有不同。详见 STL学习笔记—C++ STL算法详细介绍 (待完成)
auto n = people.count("Jack"); // 返回 2
iterator erase (const_iterator position);
\以待删除元素的迭代器作为参数
size_type erase (const key_type& k);
//以一个键作为参数,它会删除容器中所有含这个键的元素,返回容器中被移除元素的个数;
iterator erase (const_iterator first, const_iterator last);
//接受两个迭代器参数,它们指定了容器中的一段元素,这个范围内的所有元素都会被删除,这个函数返回的迭代器指向最后一个被删除元素的后一个位置。