Files
Data-Structure/std-Cpp/STL/STL-unordered_map/README.MD
2025-08-28 21:17:28 +08:00

321 lines
13 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<h1>C++ STL unordered_map容器用法详解</h1>
<div class="pre-next-page clearfix">&nbsp;</div>
<div id="arc-body"><a href='/cplus/' target='_blank'>C++</a> <a href='/stl/' target='_blank'>STL</a> 标准库中提供有 4 种无序关联式容器,本节先讲解<span style="color:#008000;"> unordered_map 容器</span><br />
<br />
unordered_map 容器,直译过来就是&quot;无序 map 容器&quot;的意思。所谓&ldquo;无序&rdquo;,指的是 unordered_map 容器不会像 map 容器那样对存储的数据进行排序。换句话说unordered_map 容器和 map 容器仅有一点不同,即 map 容器中存储的数据是有序的,而 unordered_map 容器中是无序的。<br />
<blockquote>
<p>
对于已经学过 map 容器的读者,可以将 unordered_map 容器等价为无序的 map 容器。</p>
</blockquote>
具体来讲unordered_map 容器和 map 容器一样以键值对pair类型的形式存储数据存储的各个键值对的键互不相同且不允许被修改。但由于 unordered_map 容器底层采用的是哈希表存储结构,该结构本身不具有对数据的排序功能,所以此容器内部不会自行对存储的键值对进行排序。<br />
<br />
值得一提的是unordered_map 容器在<code>&lt;unordered_map&gt;</code>头文件中,并位于 std 命名空间中。因此,如果想使用该容器,代码中应包含如下语句:<br />
<pre class="cpp">
#include &lt;unordered_map&gt;
using namespace std;</pre>
<blockquote>
<p>
注意,第二行代码不是必需的,但如果不用,则后续程序中在使用此容器时,需手动注明 std 命名空间(强烈建议初学者使用)。</p>
</blockquote>
unordered_map 容器模板的定义如下所示:
<pre class="cpp">
template &lt; class Key,//键值对中键的类型
class T,//键值对中值的类型
class Hash = hash&lt;Key&gt;,//容器内存储键值对所用的哈希函数
class Pred = equal_to&lt;Key&gt;,//判断各个键值对键相同的规则
class Alloc = allocator&lt; pair&lt;const Key,T&gt; &gt; // 指定分配器对象的类型
&gt; class unordered_map;</pre>
以上 5 个参数中,必须显式给前 2 个参数传值,并且除特殊情况外,最多只需要使用前 4 个参数,各自的含义和功能如表 1 所示。<br />
<br />
<table>
<caption>
表 1 unordered_map 容器模板类的常用参数</caption>
<tbody>
<tr>
<th>
参数</th>
<th>
含义</th>
</tr>
<tr>
<td>
&lt;key,T&gt;</td>
<td>
前 2 个参数分别用于确定键值对中键和值的类型,也就是存储键值对的类型。</td>
</tr>
<tr>
<td>
Hash = hash&lt;Key&gt;</td>
<td>
用于指明容器在存储各个键值对时要使用的哈希函数,默认使用 STL 标准库提供的 hash&lt;key&gt; 哈希函数。注意,默认哈希函数只适用于基本数据类型(包括 string 类型),而不适用于自定义的结构体或者类。</td>
</tr>
<tr>
<td>
Pred = equal_to&lt;Key&gt;</td>
<td>
要知道unordered_map 容器中存储的各个键值对的键是不能相等的,而判断是否相等的规则,就由此参数指定。默认情况下,使用 STL 标准库中提供的 equal_to&lt;key&gt; 规则,该规则仅支持可直接用 == 运算符做比较的数据类型。</td>
</tr>
</tbody>
</table>
<blockquote>
<p>
总的来说,当无序容器中存储键值对的键为自定义类型时,默认的哈希函数 hash<key> 以及比较函数 equal_to<key> 将不再适用,只能自己设计适用该类型的哈希函数和比较函数,并显式传递给 Hash 参数和 Pred 参数。至于如何实现自定义,后续章节会做详细讲解。</key></key></p>
</blockquote>
<h2>
创建C++ unordered_map容器的方法</h2>
常见的创建 unordered_map 容器的方法有以下几种。<br />
<br />
1) 通过调用 unordered_map 模板类的默认构造函数,可以创建空的 unordered_map 容器。比如:
<pre class="cpp">
std::unordered_map&lt;std::string, std::string&gt; umap;</pre>
由此,就创建好了一个可存储 &lt;string,string&gt; 类型键值对的 unordered_map 容器。<br />
<br />
2) 当然,在创建 unordered_map 容器的同时,可以完成初始化操作。比如:
<pre class="cpp">
std::unordered_map&lt;std::string, std::string&gt; umap{
{&quot;<a href='/python/' target='_blank'>Python</a>教程&quot;,&quot;http://c.biancheng.net/python/&quot;},
{&quot;Java教程&quot;,&quot;http://c.biancheng.net/java/&quot;},
{&quot;Linux教程&quot;,&quot;http://c.biancheng.net/linux/&quot;} };</pre>
通过此方法创建的 umap 容器中,就包含有 3 个键值对元素。<br />
<br />
3) 另外,还可以调用 unordered_map 模板中提供的复制(拷贝)构造函数,将现有 unordered_map 容器中存储的键值对,复制给新建 unordered_map 容器。<br />
<br />
例如,在第二种方式创建好 umap 容器的基础上,再创建并初始化一个 umap2 容器:
<pre class="cpp">
std::unordered_map&lt;std::string, std::string&gt; umap2(umap);</pre>
由此umap2 容器中就包含有 umap 容器中所有的键值对。<br />
<br />
除此之外C++ 11 标准中还向 unordered_map 模板类增加了移动构造函数,即以右值引用的方式将临时 unordered_map 容器中存储的所有键值对,全部复制给新建容器。例如:
<pre class="cpp">
//返回临时 unordered_map 容器的函数
std::unordered_map &lt;std::string, std::string &gt; retUmap(){
std::unordered_map&lt;std::string, std::string&gt;tempUmap{
{&quot;Python教程&quot;,&quot;http://c.biancheng.net/python/&quot;},
{&quot;Java教程&quot;,&quot;http://c.biancheng.net/java/&quot;},
{&quot;Linux教程&quot;,&quot;http://c.biancheng.net/linux/&quot;} };
return tempUmap;
}
//调用移动构造函数,创建 umap2 容器
std::unordered_map&lt;std::string, std::string&gt; umap2(retUmap());</pre>
注意,无论是调用复制构造函数还是拷贝构造函数,必须保证 2 个容器的类型完全相同。<br />
<br />
4) 当然,如果不想全部拷贝,可以使用 unordered_map 类模板提供的迭代器,在现有 unordered_map 容器中选择部分区域内的键值对,为新建 unordered_map 容器初始化。例如:
<pre class="cpp">
//传入 2 个迭代器,
std::unordered_map&lt;std::string, std::string&gt; umap2(++umap.begin(),umap.end());</pre>
通过此方式创建的 umap2 容器,其内部就包含 umap 容器中除第 1 个键值对外的所有其它键值对。<br />
<h2>
C++ unordered_map容器的成员方法</h2>
unordered_map 既可以看做是关联式容器,更属于自成一脉的无序容器。因此在该容器模板类中,既包含一些在学习关联式容器时常见的成员方法,还有一些属于无序容器特有的成员方法。<br />
<br />
表 2 列出了 unordered_map 类模板提供的所有常用的成员方法以及各自的功能。<br />
<br />
<table>
<caption>
表 2 unordered_map类模板成员方法</caption>
<tbody>
<tr>
<th>
成员方法</th>
<th>
功能</th>
</tr>
<tr>
<td>
begin()</td>
<td>
返回指向容器中第一个键值对的正向迭代器。</td>
</tr>
<tr>
<td>
end()&nbsp;</td>
<td>
返回指向容器中最后一个键值对之后位置的正向迭代器。</td>
</tr>
<tr>
<td>
cbegin()</td>
<td>
和 begin() 功能相同,只不过在其基础上增加了 const 属性,即该方法返回的迭代器不能用于修改容器内存储的键值对。</td>
</tr>
<tr>
<td>
cend()</td>
<td>
和 end() 功能相同,只不过在其基础上,增加了 const 属性,即该方法返回的迭代器不能用于修改容器内存储的键值对。</td>
</tr>
<tr>
<td>
empty()</td>
<td>
若容器为空,则返回 true否则 false。</td>
</tr>
<tr>
<td>
size()</td>
<td>
返回当前容器中存有键值对的个数。</td>
</tr>
<tr>
<td>
max_size()</td>
<td>
返回容器所能容纳键值对的最大个数,不同的操作系统,其返回值亦不相同。</td>
</tr>
<tr>
<td>
operator[key]</td>
<td>
该模板类中重载了 [] 运算符,其功能是可以向访问数组中元素那样,只要给定某个键值对的键 key就可以获取该键对应的值。注意如果当前容器中没有以 key 为键的键值对,则其会使用该键向当前容器中插入一个新键值对。</td>
</tr>
<tr>
<td>
at(key)</td>
<td>
返回容器中存储的键 key 对应的值,如果 key 不存在,则会抛出 out_of_range 异常。&nbsp;</td>
</tr>
<tr>
<td>
find(key)</td>
<td>
查找以 key 为键的键值对,如果找到,则返回一个指向该键值对的正向迭代器;反之,则返回一个指向容器中最后一个键值对之后位置的迭代器(如果 end() 方法返回的迭代器)。</td>
</tr>
<tr>
<td>
count(key)</td>
<td>
在容器中查找以 key 键的键值对的个数。</td>
</tr>
<tr>
<td>
equal_range(key)</td>
<td>
返回一个 pair 对象,其包含 2 个迭代器,用于表明当前容器中键为 key 的键值对所在的范围。</td>
</tr>
<tr>
<td>
emplace()</td>
<td>
向容器中添加新键值对,效率比 insert() 方法高。</td>
</tr>
<tr>
<td>
emplace_hint()</td>
<td>
向容器中添加新键值对,效率比 insert() 方法高。</td>
</tr>
<tr>
<td>
insert()&nbsp;</td>
<td>
向容器中添加新键值对。</td>
</tr>
<tr>
<td>
erase()</td>
<td>
删除指定键值对。</td>
</tr>
<tr>
<td>
clear()&nbsp;</td>
<td>
清空容器,即删除容器中存储的所有键值对。</td>
</tr>
<tr>
<td>
swap()</td>
<td>
交换 2 个 unordered_map 容器存储的键值对,前提是必须保证这 2 个容器的类型完全相等。</td>
</tr>
<tr>
<td>
bucket_count()</td>
<td>
返回当前容器底层存储键值对时,使用桶(一个线性链表代表一个桶)的数量。</td>
</tr>
<tr>
<td>
max_bucket_count()</td>
<td>
返回当前系统中unordered_map 容器底层最多可以使用多少桶。</td>
</tr>
<tr>
<td>
bucket_size(n)</td>
<td>
返回第 n 个桶中存储键值对的数量。</td>
</tr>
<tr>
<td>
bucket(key)</td>
<td>
返回以 key 为键的键值对所在桶的编号。</td>
</tr>
<tr>
<td>
load_factor()</td>
<td>
返回 unordered_map 容器中当前的负载因子。负载因子指的是的当前容器中存储键值对的数量size()和使用桶数bucket_count())的比值,即 load_factor() = size() / bucket_count()。</td>
</tr>
<tr>
<td>
max_load_factor()</td>
<td>
返回或者设置当前 unordered_map 容器的负载因子。</td>
</tr>
<tr>
<td>
rehash(n)</td>
<td>
将当前容器底层使用桶的数量设置为 n。</td>
</tr>
<tr>
<td>
reserve()</td>
<td>
将存储桶的数量(也就是 bucket_count() 方法的返回值设置为至少容纳count个元不超过最大负载因子所需的数量并重新整理容器。</td>
</tr>
<tr>
<td>
hash_function()</td>
<td>
返回当前容器使用的哈希函数对象。</td>
</tr>
</tbody>
</table>
<blockquote>
<p>
注意,对于实现互换 2 个相同类型 unordered_map 容器的键值对,除了可以调用该容器模板类中提供的 swap() 成员方法外STL 标准库还提供了同名的 swap() 非成员函数。</p>
</blockquote>
<br />
下面的样例演示了表 2 中部分成员方法的用法:<br />
<pre class="cpp">
#include &lt;iostream&gt;
#include &lt;string&gt;
#include &lt;unordered_map&gt;
using namespace std;
int main()
{
//创建空 umap 容器
unordered_map&lt;string, string&gt; umap;
//向 umap 容器添加新键值对
umap.emplace(&quot;Python教程&quot;, &quot;http://c.biancheng.net/python/&quot;);
umap.emplace(&quot;Java教程&quot;, &quot;http://c.biancheng.net/java/&quot;);
umap.emplace(&quot;Linux教程&quot;, &quot;http://c.biancheng.net/linux/&quot;);
//输出 umap 存储键值对的数量
cout &lt;&lt; &quot;umap size = &quot; &lt;&lt; umap.size() &lt;&lt; endl;
//使用迭代器输出 umap 容器存储的所有键值对
<a href='/view/1811.html' target='_blank'>for</a> (auto iter = umap.begin(); iter != umap.end(); ++iter) {
cout &lt;&lt; iter-&gt;first &lt;&lt; &quot; &quot; &lt;&lt; iter-&gt;second &lt;&lt; endl;
}
return 0;
}</pre>
程序执行结果为:
<p class="info-box">
umap size = 3<br />
Python教程 http://c.biancheng.net/python/<br />
Linux教程 http://c.biancheng.net/linux/<br />
Java教程 http://c.biancheng.net/java/</p>