news 2026/4/27 21:45:19

C++笔记·-- STL unordered_map

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
C++笔记·-- STL unordered_map

在C++ STL中,unordered_map是一种**无序关联式容器**,核心功能是存储键值对(key-value),通过键(key)快速查找对应的值(value),其底层基于哈希表实现,查找效率接近O(1),是日常开发中“键值映射”场景的首选容器之一。

本文从基础认知、初始化、核心API、底层原理、迭代器使用、与map的核心区别、常见误区及面试考点,全面整理unordered_map知识点,兼顾基础应用与应试需求,避免冗余,直击重点。

一、unordered_map 基础认知

1. 核心定义

unordered_map是STL中**无序键值对容器**,键(key)唯一,每个key对应一个value,底层依赖哈希表(哈希桶)实现,不保证键值对的存储顺序,也不支持按键的顺序遍历(与map的有序性形成核心区别)。

核心特点:

键唯一:同一个key只能出现一次,重复插入会覆盖原有value;

无序性:存储顺序与插入顺序无关,取决于key的哈希值;

高效查找:平均查找、插入、删除时间复杂度均为O(1),最坏情况O(n)(哈希冲突严重时);

不支持随机访问:无法通过下标[i]访问第i个元素(仅支持通过key访问value)。

2. 头文件与命名空间

使用unordered_map必须包含对应头文件,默认在std命名空间下(可通过using namespace std;简化使用):

#include <unordered_map> // 必须包含的头文件 using namespace std; // 可选,不写则需写 std::unordered_map

3. 适用场景

适合“通过key快速查找value”的场景,例如:

统计元素出现次数(如统计字符串中每个字符的出现次数);

键值映射(如学生ID对应学生信息、配置项key对应配置值);

快速查找、插入、删除,且不关心元素顺序的场景。

二、unordered_map 的定义与初始化

unordered_map的初始化方式与其他STL容器类似,支持多种初始化写法,重点掌握前3种常用方式:

#include <iostream> #include <unordered_map> using namespace std; int main() { // 1. 空容器初始化(最常用) unordered_map<string, int> um1; // key为string类型,value为int类型 // 2. 列表初始化(C++11及以上支持,简洁直观) unordered_map<string, int> um2 = {{"apple", 5}, {"banana", 3}, {"orange", 4}}; // 3. 拷贝初始化(复制另一个unordered_map) unordered_map<string, int> um3(um2); // 4. 范围初始化(复制另一个容器的指定范围) unordered_map<string, int> um4(um2.begin(), um2.end()); // 5. 指定哈希桶数量(优化性能,可选) unordered_map<string, int> um5(10); // 初始哈希桶数量为10 return 0; }

注意:key的类型必须支持哈希运算(如int、string、char等基础类型),自定义类型需手动实现哈希函数(后续补充)。

三、unordered_map 核心常用API(必掌握)

unordered_map的API围绕“键值对的插入、删除、查找、遍历”展开,重点掌握以下常用方法,结合代码示例理解,避免死记硬背。

1. 插入键值对(4种常用方式)

unordered_map<string, int> um; // 方式1:使用insert()插入pair对象(最规范) um.insert(pair<string, int>("apple", 5)); // 方式2:使用insert()插入make_pair(简洁) um.insert(make_pair("banana", 3)); // 方式3:使用[]赋值插入(最常用,注意:key不存在则插入,存在则覆盖value) um["orange"] = 4; // 插入键值对("orange",4) um["apple"] = 6; // 覆盖原有value,此时apple对应的值为6 // 方式4:emplace()原位构造(效率高于insert,C++11及以上) um.emplace("pear", 2); // 直接在容器内构造键值对,避免拷贝

关键注意:[]赋值时,若key不存在,会自动插入该键值对(value为对应类型的默认值,如int默认0);若key存在,会直接覆盖value。

2. 查找键值对(核心API)

unordered_map<string, int> um = {{"apple",5}, {"banana",3}}; // 方式1:使用find()查找(返回迭代器) auto it = um.find("apple"); // 查找key为"apple"的元素 if (it != um.end()) { // 找到:it->first 是key,it->second 是value cout << "找到:" << it->first << "=" << it->second << endl; } else { // 未找到:返回um.end() cout << "未找到该key" << endl; } // 方式2:使用[]查找(简洁,但有副作用) int val1 = um["apple"]; // 找到,返回对应value(5) int val2 = um["grape"]; // 未找到,自动插入("grape", 0),返回0 // 方式3:使用count()判断key是否存在(返回0或1,因为key唯一) if (um.count("banana")) { cout << "banana存在" << endl; } else { cout << "banana不存在" << endl; }

重点:find()是最安全的查找方式,不会修改容器;[]查找有副作用(未找到会插入),需谨慎使用;count()仅判断存在性,不返回value。

3. 删除键值对

unordered_map<string, int> um = {{"apple",5}, {"banana",3}, {"orange",4}}; // 方式1:通过key删除(最常用) um.erase("apple"); // 删除key为"apple"的键值对 // 方式2:通过迭代器删除 auto it = um.find("banana"); if (it != um.end()) { um.erase(it); // 删除迭代器指向的键值对 } // 方式3:删除所有元素(清空容器) um.clear(); // 方式4:删除指定范围(迭代器范围) um.erase(um.begin(), um.end()); // 等价于clear()

4. 容量与大小操作

方法

作用

示例

size()

获取容器中键值对的个数

um.size() → 返回当前元素个数

empty()

判断容器是否为空(空返回true)

if (um.empty()) { ... }

bucket_count()

获取哈希桶的数量(底层相关)

um.bucket_count() → 查看当前哈希桶个数

5. 其他常用API

um.swap(um2):交换两个unordered_map的内容;

um.max_size():获取容器最大可存储的键值对个数(系统限制);

um.at(key):通过key访问value,若key不存在,抛异常(比[]安全)。

四、unordered_map 迭代器(结合之前所学迭代器知识)

结合STL迭代器的基础,unordered_map的迭代器属于**双向迭代器**(与list、map一致),不支持随机访问(不能用it + n[]),仅支持++it--it移动。

1. 迭代器类型

unordered_map<K, V>::iterator:非const迭代器,可读写键值对(key不可修改,value可修改);

unordered_map<K, V>::const_iterator:const迭代器,只读,不可修改键值对;

unordered_map<K, V>::reverse_iterator:反向迭代器,从容器末尾开始遍历。

关键注意:unordered_map的key是不可修改的(因为key的哈希值决定了其存储位置,修改key会导致哈希冲突),因此即使是普通迭代器,也不能修改it->first(key),只能修改it->second(value)。

2. 迭代器遍历(常用写法)

unordered_map<string, int> um = {{"apple",5}, {"banana",3}, {"orange",4}}; // 方式1:普通迭代器遍历(可修改value) for (unordered_map<string, int>::iterator it = um.begin(); it != um.end(); ++it) { cout << it->first << "=" << it->second << " "; it->second += 1; // 可修改value } // 方式2:C++11 auto简化(最常用) for (auto it = um.begin(); it != um.end(); ++it) { cout << it->first << "=" << it->second << " "; } // 方式3:const迭代器(只读,不可修改) for (auto it = um.cbegin(); it != um.cend(); ++it) { cout << it->first << "=" << it->second << " "; // it->second = 10; // 报错,只读迭代器 } // 方式4:范围for遍历(C++11及以上,最简洁) for (auto& pair : um) { // 用引用避免拷贝,提高效率 cout << pair.first << "=" << pair.second << " "; }

五、unordered_map 底层原理(面试核心)

unordered_map的核心优势的是“高效查找”,其底层依赖**哈希表(哈希桶模型)** 实现,这也是它与map(红黑树实现)的核心区别。

1. 底层结构:哈希桶模型

底层由两部分组成:

哈希桶数组(vector实现):存储多个“桶”,每个桶对应一个链表(或红黑树,C++11后优化);

链表/红黑树:每个桶中存储“哈希值相同的键值对”(解决哈希冲突)。

核心流程:

  1. 插入键值对时,通过key的哈希函数,计算出对应的哈希值;

  2. 通过哈希值取模(对哈希桶数组大小取模),确定该键值对要放入的“桶”;

  3. 若该桶中没有哈希冲突(无相同哈希值的key),直接插入;若有冲突,插入到桶对应的链表/红黑树中;

  4. 查找时,同样通过key计算哈希值、确定桶,再遍历桶中的链表/红黑树,找到对应key。

2. 哈希冲突与解决方式

哈希冲突:不同的key,通过哈希函数计算出相同的哈希值,导致要放入同一个桶中。

STL中unordered_map解决哈希冲突的方式:链地址法(桶+链表/红黑树):

当桶中元素较少时,用链表存储冲突元素;

当桶中元素较多(超过阈值),链表会自动转为红黑树,将查找时间复杂度从O(n)优化为O(logn)。

3. 底层优势与劣势

  • 优势:平均查找、插入、删除效率O(1),比map(O(logn))更快;

  • 劣势:

    • 无序存储,无法按key排序;

    • 哈希函数有开销,且存在哈希冲突,最坏情况效率下降为O(n);

    • 内存占用比map高(需要存储哈希桶、链表/红黑树结构)。

六、unordered_map 与 map 核心对比(面试必背)

unordered_mapmap都是键值对容器,key唯一,但底层实现、性能、有序性差异极大,是面试高频考点,用表格清晰对比:

对比项

unordered_map

map

底层实现

哈希表(桶+链表/红黑树)

红黑树(有序平衡二叉树)

有序性

无序(存储顺序与插入顺序无关)

有序(按key升序排列,可自定义排序)

查找效率

平均O(1),最坏O(n)

O(logn)(稳定)

插入/删除效率

平均O(1),最坏O(n)

O(logn)

迭代器类型

双向迭代器

双向迭代器

key是否可修改

不可修改

不可修改

内存占用

较高(哈希桶+冲突解决结构)

较低(仅红黑树节点)

适用场景

无需排序,追求高效查找/插入

需要按key排序,查找效率要求稳定

七、常见误区与避坑重点

1. 误区1:unordered_map 支持随机访问

错误:unordered_map不支持随机访问,无法通过um[i]访问第i个元素,只能通过um[key]访问对应value;

正确:随机访问仅支持vector、deque,unordered_map、map、list均不支持。

2. 误区2:[] 查找不会修改容器

错误:用um[key]查找时,若key不存在,会自动插入该键值对(value为默认值),修改容器内容;

正确:仅判断key是否存在,用count();安全查找value,用find()at()(at()不存在抛异常)。

3. 误区3:key可以修改

错误:无论是unordered_map还是map,key都不可修改(修改key会破坏底层结构:哈希表的存储位置、红黑树的有序性);

正确:若需修改key,需先删除原键值对,再插入新的键值对。

4. 误区4:哈希冲突会导致程序崩溃

错误:STL已内置哈希冲突解决机制(链地址法),哈希冲突只会影响效率,不会导致程序崩溃;

优化:可通过reserve(n)提前设置哈希桶数量,减少哈希冲突(n为预计存储的元素个数)。

八、总结(核心考点浓缩,面试直接背)

  1. unordered_map是STL**无序键值对容器**,key唯一,底层基于哈希表实现,平均查找/插入/删除效率O(1);

  2. 核心API:insert()/emplace()插入、find()查找、erase()删除、[]/at()访问value;

  3. 迭代器:双向迭代器,不支持随机访问,key不可修改,value可修改;

  4. 底层:哈希桶+链地址法解决哈希冲突,冲突严重时链表转红黑树;

  5. 与map区别:无序vs有序、O(1)vsO(logn)效率、高内存vs低内存;

  6. 常见坑:[]查找有副作用、不支持随机访问、key不可修改;

  7. 面试高频:底层实现(哈希桶)、与map的区别、哈希冲突解决方式、迭代器类型。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/27 21:44:28

Seraphine:英雄联盟玩家的智能辅助助手完整指南

Seraphine&#xff1a;英雄联盟玩家的智能辅助助手完整指南 【免费下载链接】Seraphine 英雄联盟战绩查询工具 项目地址: https://gitcode.com/gh_mirrors/se/Seraphine 你是否曾经在英雄联盟对局中因为错过接受匹配而懊恼&#xff1f;是否在BP阶段犹豫不决&#xff0c;…

作者头像 李华
网站建设 2026/4/27 21:41:56

多实例生成技术:身份保持与生成灵活性的平衡

1. 多实例生成技术概述多实例生成&#xff08;Multi-Instance Generation&#xff09;是计算机视觉领域近年来快速发展的研究方向&#xff0c;其核心目标是从一组参考图像中提取特征并生成新的图像实例&#xff0c;同时保持参考主体的身份特征。这项技术在虚拟角色生成、广告设…

作者头像 李华
网站建设 2026/4/27 21:40:26

NLP自动化数据增强技术:原理、实践与性能提升

1. 项目概述&#xff1a;自动化数据增强如何提升NLP模型性能在自然语言处理领域&#xff0c;数据质量往往决定着模型性能的天花板。我最近在多个文本分类项目中反复验证了一个现象&#xff1a;当训练数据量不足或样本分布不均衡时&#xff0c;即使采用最先进的预训练模型&#…

作者头像 李华
网站建设 2026/4/27 21:38:46

并行线性求解器在最优控制中的高效实现与优化

1. 并行线性求解器在最优控制中的关键作用现代最优控制问题&#xff08;如机器人轨迹规划、自动驾驶决策等&#xff09;通常需要实时求解大规模线性方程组。这类问题在模型预测控制&#xff08;MPC&#xff09;框架下会转化为块三对角结构的线性系统&#xff0c;其求解效率直接…

作者头像 李华
网站建设 2026/4/27 21:37:43

全自动防爆气象站监测系统

Ex ia IIC T6 Ga级防爆认证&#xff0c;安全合规&#xff1a;整机通过Ex ia IIC T6 Ga级防爆认证&#xff0c;可直接部署于化工厂IIC级危险区域&#xff08;涵盖大部分可燃气体、有毒气体环境&#xff09;&#xff0c;T6级最高温度组别&#xff0c;适配高温化工场景&#xff0c…

作者头像 李华