目录
3.1 constructor、destructor、operator=
map
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;
map是关联式容器,它按照特定的顺序存储由关键字值和映射值的组合形成的元素。
在map中,关键字值通常用于排序和唯一识别元素,而映射值则存储与此键相关的内容。关键字值和映射值的类型可能不同,在成员类型value_type中被组合在一起,它是一个结合了两者的pair类型:
typedef pair<const Key, T> value_type;
在内部,map中的元素总是按照它的关键字进行排序,遵循一个特定的严格的弱排序标准,由其内部的比较对象(类型为Compare)指示。
map容器通常比unordered_map容器按关键字访问单个元素的速度要慢,但是它们允许根据顺序直接迭代子集。
map中的映射值可以使用括号操作符(operator[])直接通过其相应的关键字来访问。
map通常以二叉搜索树(准确地说,是红黑树)的形式实现。
map定义在头文件map和命名空间std中。
1. pair类型
pair定义在头文件utility和命名空间std中。
一个pair保存两个数据成员。类似容器,pair是一个用来生成特定类型的模板。当创建一个pair时,我们必须提供两个类型名,pair的数据成员将具有对应的类型。两个类型不要求一样:
pair<string, string> anon; // 保存两个string
pair<string, size_t> word_count; // 保存一个string和一个size_t
pair<string, vector<int>> line; // 保存string和vector<int>
pair的默认构造函数对数据成员进行值初始化。因此,anon是一个包含两个空string的pair,line保存一个空string和一个空vector。word_count中的size_t成员值为0,而string成员被初始化为空。
我们也可以为每个成员提供初始化器:
pair<string, string> author{ "James","Joyce" };
这条语句创建一个名为author的pair,两个成员被初始化为"James"和"Joyce"。
与其他标准库类型不同,pair的数据成员是public的。两个成员分别命名为first和second。我们用普通的成员访问符号来访问它们。
pair<T1, T2> p; | p是一个pair,两个类型分别为T1和T2的成员都进行了值初始化 |
pair<T1, T2> p(v1, v2); | p是一个成员类型为T1和T2的pair first和second成员分别用v1和v2进行初始化 |
pair<T1, T2> p = { v1,v2 }; | 等价于pair<T1, T2> p(v1, v2); |
make_pair(v1, v2) | 返回一个用v1和v2初始化的pair pair的类型从v1和v2的类型推断出来 |
p.first | 返回p的名为first的(公有)数据成员 |
p.second | 返回p的名为second的(公有)数据成员 |
p1 relop p2 | 关系运算符(<、>、<=、>=)按字典序定义 |
p1 == p2 p1 != p2 | 当first和second成员分别相等时,两个pair相等 相等性判断利用元素的==运算符实现 |
创建pair对象的函数:
想象有一个函数需要返回一个pair。在新标准下,我们可以对返回值进行列表初始化:
pair<string, int> process(vector<string>& v)
{
// 处理v
if (!v.empty())
return { v.back(), v.back().size() }; // 列表初始化
else
return pair<string, int>(); // 隐式构造返回值
}
若v不为空,我们返回一个由v中最后一个string及其大小组成的pair。否则,隐式构造一个空pair,并返回它。
在较早的C++版本中,不允许用花括号包围的初始化器来返回pair这种类型的对象,必须显式构造返回值:
if (!v.empty())
return pair<string, int>(v.back(), v.back().size());
我们还可以用make_pair来生成pair对象,pair的两个类型来自于make_pair的参数:
if (!v.empty())
return make_pair(v.back(), v.back().size());
2. 关联式容器额外的类型别名
key_type | 此容器类型的关键字类型 |
mapped_type | 每个关键字关联的类型;只适用于map |
value_type | 对于set,与key_type相同 对于map,为pair<const key_type, mapped_type> |
对于set类型,key_type和value_type是一样的:set中保存的值就是关键字。在一个map中,元素是键值对。即,每个元素是一个pair对象,包含一个关键字和一个关联的值。由于我们不能改变一个元素的关键字,因此这些pair的关键字部分是const的:
set<string>::value_type vl; // v1是一个string
set<string>::key_type v2; // v2是一个string
map<string, int>::value_type v3; // v3是一个pair<const string, int>
map<string, int>::key_type v4; // v4是一个string
map<string, int>::mapped_type v5; // v5是一个int
与序列式容器一样,我们使用作用域运算符来提取一个类型的成员——例如,map<string, int>::key_type。
只有map类型(unordered_map、unordered_multimap、multimap和map)才定义了mapped_type。
3. Member functions
3.1 constructor、destructor、operator=
3.1.1 constructor
// empty (1)
explicit map(const key_compare& comp = key_compare(), const allocator_type& alloc = allocator_type());
explicit map(const allocator_type& alloc);
// 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);
map(const map& x, const allocator_type& alloc);
// move (4)
map(map&& x);
map(map&& x, const allocator_type& alloc);
// initializer list (5)
map(initializer_list<value_type> il, const key_compare& comp = key_compare(), const allocator_type& alloc = allocator_type());
创建降序排序的空map:
#include <map>
#include <iostream>
using namespace std;
struct cmp
{
bool operator()(const int e1, const int e2) const
{
return e1 > e2;
}
};
int main()
{
map<char, int, cmp> mp;
// 插入
mp.insert({ 'g',30 });
mp.insert(make_pair('a', 10));
mp.insert(pair<char, int>('f', 50));
mp.insert(map<char, int>::value_type('h', 70));
mp['b'];
mp['d'] = 20;
for (auto& [x, y] : mp) // C++17 结构化绑定
{
cout << x << " " << y << endl;
}
// h 70
// g 30
// f 50
// d 20
// b 0
// a 10
return 0;
}
3.1.2 destructor
~map();
3.1.3 operator=
// copy (1)
map& operator=(const map& x);
// move (2)
map& operator=(map&& x);
// initializer list (3)
map& operator=(initializer_list<value_type> il);
3.2 Iterators
// begin
iterator begin() noexcept;
const_iterator begin() const noexcept;
// end
iterator end() noexcept;
const_iterator end() const noexcept;
// rbegin
reverse_iterator rbegin() noexcept;
const_reverse_iterator rbegin() const noexcept;
// rend
reverse_iterator rend() noexcept;
const_reverse_iterator rend() const noexcept;
// cbegin
const_iterator cbegin() const noexcept;
// cend
const_iterator cend() const noexcept;
// crbegin
const_reverse_iterator crbegin() const noexcept;
// crend
const_reverse_iterator crend() const noexcept;
函数 | 功能 |
---|---|
& | begin返回一个迭代器,指向map中第一个元素 end返回一个迭代器,指向map中最后一个元素的下一个位置 |
& | rbegin返回一个反向迭代器,指向map中最后一个元素 rend返回一个反向迭代器,指向map中第一个元素的上一个位置 |
& | cbegin返回一个const迭代器,指向map中第一个元素 cend返回一个const迭代器,指向map中最后一个元素的下一个位置 |
& | crbegin返回一个const反向迭代器,指向map中最后一个元素 crend返回一个const反向迭代器,指向map中第一个元素的上一个位置 |
begin&end和rbegin&rend返回的迭代器指向:
const_iterator是一个指向const内容的迭代器。迭代器本身可以修改,但是它不能被用来修改它所指向的内容。
begin&end/rbegin&rend和cbegin&cend/crbegin&crend的不同:
- begin&end/rbegin&rend的返回类型由对象是否是常量来决定。如果不是常量,返回iterator;如果是常量,返回const_iterator。
- cbegin&cend/crbegin&crend的返回类型是const_iterator,不管对象本身是否是常量。
当解引用一个关联式容器迭代器时,我们会得到一个类型为容器的value_type的值的引用。对map而言,value_type是一个pair类型,其first成员保存const的关键字,second成员保存值。
必须记住,一个map的value_type是一个pair,我们可以改变pair的值,但不能改变关键字成员的值。
#include <iostream>
#include <map>
using namespace std;
int main()
{
map<string, string> mp{ {"iterator", "迭代器"},{"begin","开始"},{"end", "结束"} };
map<string, string>::iterator it = mp.begin();
while (it != mp.end())
{
cout << it->first << " " << it->second << endl;
// 等价于cout << (*it).first << " " << (*it).second << endl;
++it;
}
// begin 开始
// end 结束
// iterator 迭代器
auto rit = mp.rbegin();
// map<string, string>::reverse_iterator rit = mp.rbegin();
while (rit != mp.rend())
{
cout << rit->first << " " << rit->second << endl;
// 等价于cout << (*rit).first << " " << (*rit).second << endl;
++rit;
}
// iterator 迭代器
// end 结束
// begin 开始
return 0;
}
3.3 Capacity
3.3.1 empty
bool empty() const noexcept;
// 检测map是否为空,是返回true,否则返回false
3.3.2 size
size_type size() const noexcept;
// 返回map中元素的个数
3.3.3 max_size
size_type max_size() const noexcept;
// 返回map能够容纳的最大元素个数
3.4 Element access
3.4.1 operator[]
mapped_type& operator[](const key_type& k);
mapped_type& operator[](key_type&& k);
// 如果k与某个元素的关键字相匹配,返回对其映射值的引用
// 如果k与任何元素的关键字不匹配,插入一个关键字为k的新元素,并返回对其映射值的引用
3.4.2 at
mapped_type& at(const key_type& k);
const mapped_type& at(const key_type& k) const;
// 如果k与某个元素的关键字相匹配,返回对其映射值的引用
// 如果k与任何元素的关键字不匹配,抛异常
Element access系列函数使用示例:
#include <map>
#include <iostream>
using namespace std;
int main()
{
map<char, int> mp;
mp['b']; // 插入一个关键字为'b'的元素,但没有对映射值进行初始化
mp['d'] = 20; // 插入一个关键字为'd'的元素,并将映射值初始化为20
cout << "mp['b'] = " << mp['b'] << endl; // mp['b'] = 0
cout << "mp['d'] = " << mp['d'] << endl; // mp['d'] = 20
mp['b'] = 50; // 修改关键字为'b'的元素的映射值为50
cout << "mp['b'] = " << mp.at('b') << endl; // mp['b'] = 50
cout << "mp['d'] = " << mp.at('d') << endl; // mp['d'] = 20
return 0;
}
3.5 Modifiers
3.5.1 insert
// single element (1) 成功返回pair<插入位置, true>,失败返回pair<插入位置, false>
pair<iterator, bool> insert(const value_type& val);
template <class P> pair<iterator, bool> insert(P&& val);
// with hint (2)
iterator insert(const_iterator position, const value_type& val);
template <class P> iterator insert(const_iterator position, P&& val);
// range (3)
template <class InputIterator> void insert(InputIterator first, InputIterator last);
// initializer list (4)
void insert(initializer_list<value_type> il);
// 插入
3.5.2 erase
// (1)
iterator erase(const_iterator position);
// (2)
size_type erase(const key_type& k);
// (3)
iterator erase(const_iterator first, const_iterator last);
// 删除
3.5.3 swap
void swap(map& x);
// 交换
3.5.4 clear
void clear() noexcept;
// 清空
3.5.5 emplace
template <class... Args> pair<iterator, bool> emplace(Args&&... args);
// 对应insert,区别是:
// 当调用insert时,我们将元素类型的对象传递给它们,这些对象被拷贝到容器中
// 当调用emplace时,则是将参数传递给元素类型的构造函数,然后使用这些参数在容器管理的内存空间中直接构造元素
3.5.6 emplace_hint
template <class... Args> iterator emplace_hint(const_iterator position, Args&&... args);
// 对应insert的with hint(2),区别是:
// 当调用insert时,我们将元素类型的对象传递给它们,这些对象被拷贝到容器中
// 当调用emplace时,则是将参数传递给元素类型的构造函数,然后使用这些参数在容器管理的内存空间中直接构造元素
3.6 Observers
3.6.1 key_comp
key_compare key_comp() const;
// 返回关键字比较对象(决定容器中元素的顺序)
3.6.2 value_comp
value_compare value_comp() const;
// 返回值比较对象(决定容器中元素的顺序)
3.7 Operations
3.7.1 find
iterator find(const key_type& k);
const_iterator find(const key_type& k) const;
// 返回一个迭代器,指向第一个关键字为k的元素,若k不在容器中,则返回end迭代器
3.7.2 count
size_type count(const key_type& k) const;
// 返回关键字等于k的元素的数量
// 对于不允许重复关键字的容器,返回值永远是0或1
#include <map>
#include <iostream>
using namespace std;
int main()
{
map<char, int> mp;
mp.insert({ 'g',30 });
mp.insert(make_pair('a', 10));
mp.insert(pair<char, int>('f', 50));
mp['b'];
mp['d'] = 20;
auto it = mp.find('f');
if (it != mp.end())
{
cout << "f在map中" << endl;
}
else
{
cout << "f不在map中" << endl;
}
// f在map中
it = mp.find('h');
if (it != mp.end())
{
cout << "h在map中" << endl;
}
else
{
cout << "h不在map中" << endl;
}
// h不在map中
if (mp.count('a'))
{
cout << "a在map中" << endl;
}
else
{
cout << "a不在map中" << endl;
}
// a在map中
if (mp.count('c'))
{
cout << "c在map中" << endl;
}
else
{
cout << "c不在map中" << endl;
}
// c不在map中
return 0;
}
3.7.3 lower_bound
iterator lower_bound(const key_type& k);
const_iterator lower_bound(const key_type& k) const;
// 返回一个迭代器,指向第一个关键字不小于k的元素
3.7.4 upper_bound
iterator upper_bound(const key_type& k);
const_iterator upper_bound(const key_type& k) const;
// 返回一个迭代器,指向第一个关键字大于k的元素
3.7.5 equal_range
pair<const_iterator, const_iterator> equal_range(const key_type& k) const;
pair<iterator, iterator> equal_range(const key_type& k);
// 返回一个迭代器pair,表示关键字等于k的元素的范围(左闭右开的区间)
// 若k不存在,pair的两个成员均为end迭代器
// 对于不允许重复关键字的容器,返回的范围最多只包含一个元素
3.8 Allocator
3.8.1 get_allocator
allocator_type get_allocator() const noexcept;
// 返回空间配置器
4. map对象的插入方法
#include <map>
#include <iostream>
using namespace std;
int main()
{
map<char, int> mp;
// 插入
mp.insert({ 'g',30 });
mp.insert(make_pair('a', 10));
mp.insert(pair<char, int>('f', 50));
mp.insert(map<char, int>::value_type('h', 70));
mp['b'];
mp['d'] = 20;
for (auto& e : mp)
{
cout << e.first << " " << e.second << endl;
}
// a 10
// b 0
// d 20
// f 50
// g 30
// h 70
return 0;
}
5. map对象的遍历方法
5.1 迭代器
#include <map>
#include <iostream>
using namespace std;
int main()
{
map<char, int> mp;
// 插入
mp.insert({ 'g',30 });
mp.insert(make_pair('a', 10));
mp.insert(pair<char, int>('f', 50));
mp.insert(map<char, int>::value_type('h', 70));
mp['b'];
mp['d'] = 20;
map<char, int>::iterator it = mp.begin();
while (it != mp.end())
{
cout << it->first << " " << it->second << endl;
++it;
}
// a 10
// b 0
// d 20
// f 50
// g 30
// h 70
return 0;
}
5.2 范围for
#include <map>
#include <iostream>
using namespace std;
int main()
{
map<char, int> mp;
// 插入
mp.insert({ 'g',30 });
mp.insert(make_pair('a', 10));
mp.insert(pair<char, int>('f', 50));
mp.insert(map<char, int>::value_type('h', 70));
mp['b'];
mp['d'] = 20;
for (auto& e : mp)
{
cout << e.first << " " << e.second << endl;
}
// a 10
// b 0
// d 20
// f 50
// g 30
// h 70
return 0;
}
#include <map>
#include <iostream>
using namespace std;
int main()
{
map<char, int> mp;
// 插入
mp.insert({ 'g',30 });
mp.insert(make_pair('a', 10));
mp.insert(pair<char, int>('f', 50));
mp.insert(map<char, int>::value_type('h', 70));
mp['b'];
mp['d'] = 20;
for (auto& [x, y] : mp) // C++17 结构化绑定
{
cout << x << " " << y << endl;
}
// a 10
// b 0
// d 20
// f 50
// g 30
// h 70
return 0;
}