当前位置: 首页 > article >正文

std::unordered_map(C++)

std::unordered_map

  • 1. 概述
  • 2. 内部实现
  • 3. 性能特征
  • 4. 常用 API
  • 5. 使用示例
  • 6. 自定义哈希与相等比较
  • 7. 注意事项与优化
  • 8. 使用建议
  • 9. emplace和insert异同
    • 相同点
    • 不同点
    • 例子对比
    • 何时优先使用哪种?

1. 概述

  • 定义:std::unordered_map<Key, T, Hash, KeyEqual, Allocator> 是一个基于哈希表(hash table)实现的键值对容器,提供平均 O(1) 的查找、插入和删除性能。

  • 特点:

    • 无序:元素按哈希值分布在若干“桶”(bucket)中,不保证遍历顺序。

    • 唯一键:同一个键最多只能出现一次;如果插入已存在的键,默认不覆盖(可用 operator[] 或 at 修改)。

    • 可自定义:支持自定义哈希函数和键相等比较器,也可指定内存分配器。

2. 内部实现

  1. 哈希桶结构

    • 容器内部维护一组“桶”,每个桶是一个链表(或其他冲突解决结构)。

    • 元素根据 Hash(key) % bucket_count 落入对应桶中。

  2. 装载因子(load factor)

    • 定义为 size() / bucket_count,表示平均每个桶的元素数。

    • 当装载因子超过 max_load_factor() 时,容器会自动进行 rehash(扩容并重新分配桶)。

  3. 再哈希(rehash)

    • 调用 rehash(n) 会将桶数调整为 ≥ n,并将所有元素重新分布。

    • reserve(n) 则是以元素数 n 为基准,确保 bucket_count ≥ n / max_load_factor()。

3. 性能特征

操作平均复杂度最坏情况复杂度备注
find / operator[]O(1)O(n)取决于哈希冲突;若所有元素落在同一桶,退化为链表查找
insert / emplaceO(1)O(n)同上,且可能触发一次 rehash(O(n))
erase(key)O(1)O(n)同上
clearO(n)O(n)
遍历(iteration)O(n + bucket_count)O(n + bucket_count)需跳过空桶
  • 空间开销:

    • 每个桶需存储一个指针或链表头;元素节点通常包含键、值、下一个节点指针,以及可能的桶索引/哈希缓存。
  • 哈希函数好坏:

    • 高质量的哈希函数能显著降低冲突,提高稳定性;如对自定义类型可使用 std::hash 或第三方的高性能哈希。

4. 常用 API

// 构造与析构
std::unordered_map<Key, T> m1;                              // 默认构造
std::unordered_map<Key, T> m2(100);                         // 指定初始桶数
std::unordered_map<Key, T> m3(100, MyHash{}, MyEqual{});    // 指定哈希与比较
// 大小与容量
bool empty() const;
size_t size() const;
size_t bucket_count() const;
float load_factor() const;
float max_load_factor() const;
void rehash(size_t newBucketCount);
void reserve(size_t count);  // 保证能插入 count 个元素而不触发 rehash
// 元素访问
T& operator[](const Key& key);       // 若不存在则插入默认构造的 T
T& at(const Key& key);               // 若不存在抛出 std::out_of_range
// 插入
std::pair<iterator, bool> insert(const value_type& kv);
template< class... Args >
std::pair<iterator, bool> emplace(Args&&... args);
// 查找与删除
iterator find(const Key& key);
size_t erase(const Key& key);
iterator erase(iterator pos);
void clear();
// 遍历
iterator begin() noexcept;
iterator end() noexcept;
const_iterator cbegin() const noexcept;
const_iterator cend() const noexcept;
// 哈希与比较器查询
size_t bucket(const Key& key) const;
size_t bucket_size(size_t n) const;
Hash hash_function() const;
KeyEqual key_eq() const;

5. 使用示例

#include <unordered_map>
#include <string>
#include <iostream>int main() {// 1. 创建并插入std::unordered_map<std::string, int> wordCount;wordCount["hello"] = 1;                // operator[] 插入或修改wordCount.insert({"world", 2});         // insert 不会覆盖已存在键wordCount.emplace("foo", 3);            // 完美转发构造// 2. 查找auto it = wordCount.find("world");if (it != wordCount.end()) {std::cout << it->first << ": " << it->second << "\n";}// 3. 遍历for (auto& kv : wordCount) {std::cout << kv.first << " => " << kv.second << "\n";}// 4. 保证容量wordCount.reserve(1000); // 预计要插入 1000 条记录,减少 rehash 次数// 5. 统计装载因子std::cout << "load factor: " << wordCount.load_factor() << "\n";return 0;
}

6. 自定义哈希与相等比较

当键类型为自定义结构体时,需要提供 Hash 和 KeyEqual:

struct Point { int x, y; };// 自定义哈希:结合 x,y 的值
struct PointHash {size_t operator()(Point const& p) const noexcept {// 经典做法:位移与异或return std::hash<int>()(p.x) ^ (std::hash<int>()(p.y) << 1);}
};// 自定义相等比较
struct PointEqual {bool operator()(Point const& a, Point const& b) const noexcept {return a.x == b.x && a.y == b.y;}
};std::unordered_map<Point, std::string, PointHash, PointEqual> mp;

7. 注意事项与优化

  • 避免频繁 rehash

    • 使用 reserve() 预先分配足够桶数,比频繁自动扩容更高效。
  • 迭代顺序不稳定

    • 容器内部槽位和元素分布会随 rehash 而变化,不要依赖遍历顺序。
  • 哈希攻击

    • 在对抗恶意输入的场景中,默认 std::hash 可能遭受冲突攻击,可考虑使用带随机种子的哈希或第三方库(如 CityHash、MurmurHash)。
  • 线程安全

    • 读写同一容器需加锁;C++20 起可在不同线程同时读不同桶,但标准未强制保证,生产环境仍建议加互斥。
  • 内存占用

    • 哈希表相较于红黑树(std::map)通常占用更多内存;应在性能 vs. 空间之间权衡。

8. 使用建议

  • 需要

    • 快速查找/插入/删除 且无需有序遍历时,首选 std::unordered_map。

    • 容器大小可预估,且希望通过 reserve() 控制 rehash 时机。

  • 不需要

    • 保持元素有序或按键排序输出时,应选用 std::map。

    • 需要对容器做范围算法(如二分查找)或有序区间操作时。

9. emplace和insert异同

相同点

  • 功能

    • 都是在 std::unordered_map 中插入元素(键值对)。

    • 若指定键已存在,插入操作不生效,都会返回相同的迭代器和 false。

  • 返回值

    • std::pair<iterator,bool>——iterator 指向新插入或已有元素的位置,bool 表示此次调用是否实际插入了新元素。

不同点

特性insertemplace
接口签名多重重载,典型如 insert(const value_type&)、insert(value_type&&)、insert(initializer_list<value_type>)template<class… Args> emplace(Args&&… args)
参数需要先构造好 value_type(即 pair<const Key, T>)再传入,可能多一次拷贝/移动直接将参数完美转发(perfect‑forward)给底层元素构造函数,就地构造,避免不必要的临时对象
构造方式拷贝 或 移动 已有的 pair原地(in‑place)调用 pair 的构造函数
效率如果要构造 pair,就至少一次临时对象(拷贝/移动)零或更少的拷贝/移动,适合复杂类型或昂贵拷贝的场景
复杂构造支持只能插入已经准备好的 pair支持 piecewise_construct,可以分别传递给 key 和 value 的构造参数

例子对比

std::unordered_map<std::string, std::vector<int>> m;// 1. insert:需要先构造一个 pair,再插入
std::pair<const std::string, std::vector<int>> p("key", {1,2,3});
auto res1 = m.insert(p);                     // 拷贝 p
auto res2 = m.insert({ "key2", {4,5,6} });    // 构造临时 pair,再移动或拷贝// 2. emplace:直接传递构造参数,就地构造
auto res3 = m.emplace("key3", std::vector<int>{7,8,9});
// 等价于:在内部执行 pair("key3", vector{7,8,9}) 的就地构造,无额外拷贝// 3. emplace + piecewise_construct(针对 key 和 value 各自参数包更复杂的场景)
struct Key { Key(int,a){} };
struct Val { Val(double,b){} };std::unordered_map<Key, Val> m2;
m2.emplace(std::piecewise_construct,std::forward_as_tuple(123),      // Key(int)std::forward_as_tuple(3.14)      // Val(double)
);

何时优先使用哪种?

  • 简单场景:插入已有 pair 或者初学者,为了可读性,用 insert 也很直观。

  • 性能敏感或避免临时:当 T 构造/拷贝/移动开销较大时,优先 emplace。

  • 复杂构造:需要传多个参数给 key 或 value 的构造函数时,emplace(尤其配合 piecewise_construct)更加灵活。

相关文章:

std::unordered_map(C++)

std::unordered_map 1. 概述2. 内部实现3. 性能特征4. 常用 API5. 使用示例6. 自定义哈希与相等比较7. 注意事项与优化8. 使用建议9. emplace和insert异同相同点不同点例子对比何时优先使用哪种&#xff1f; 1. 概述 定义&#xff1a;std::unordered_map<Key, T, Hash, KeyE…...

【MCP教程】Claude Desktop 如何连接部署在远程的remote mcp server服务器(remote host)

前言 最近MCP特别火热&#xff0c;笔者自己也根据官方文档尝试了下。 官方文档给的Demo是在本地部署一个weather.py&#xff0c;然后用本地的Claude Desktop去访问该mcp服务器&#xff0c;从而完成工具的调用&#xff1a; 但是&#xff0c;问题来了&#xff0c;Claude Deskto…...

Android Input——输入事件回调完成(十四)

前面几篇文章介绍了事件回调的相关流程,以及回调事件处理函数的相关内容,最后我们再来看一下事件处理完后,如何通知 InputDispatcher 去回调 Callback。 一、客户端回调 在 Android 的事件分发机制中,当客户端(即应用层)完成事件处理后,最终会调用 ViewRootImpl 的 fin…...

数据通信学习笔记之OSPF配置命令

华为 [huawei]ospf 10 router-id 1.1.1.1 //创建ospf进程&#xff0c;本地有效area 1 // 进入区域1network 192.168.1.0 0.0.0.255 // 宣告网段&#xff0c;使用反掩码stub // 配置为stub区域stub no-summary // 配置为Totally Stub 完全末节区域。在ABR上配置&#xff0…...

Python -yield 在python 中什么意思

在 Python 中&#xff0c;yield 是一个关键字&#xff0c;用于定义生成器函数&#xff08;generator function&#xff09;。它的作用是将一个普通函数转变为可迭代的生成器&#xff0c;具有惰性计算的特性。以下是关键要点&#xff1a; 核心概念 生成器函数&#xff1a; 当函数…...

多个路由器互通(静态路由)无单臂路由(简单版)

多个路由器互通&#xff08;静态路由&#xff09;无单臂路由&#xff08;简单版&#xff09; 开启端口并配ip地址 维护1 Router>en Router#conf t Router(config)#int g0/0 Router(config-if)#no shutdown Router(config-if)#ip address 192.168.10.254 255.255.255.0 Ro…...

OpenCV 图形API(38)图像滤波-----Sobel 算子操作函数Sobel()

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 cv::gapi::Sobel 函数是 OpenCV 的 G-API 模块中用于执行 Sobel 算子操作的一个函数&#xff0c;主要用于图像的边缘检测。Sobel 算子通过计算图…...

3DS 转 STL 全攻略:传统工具与迪威模型网在线转换深度解析

在 3D 建模与 3D 打印的技术领域中&#xff0c;常常会遇到需要将不同格式的文件进行转换的情况。其中&#xff0c;把 3DS 文件转换为 STL 格式是较为常见的操作。3DS 文件作为一种旧版 Autodesk 3D Studio 使用的 3D 图像格式&#xff0c;存储着丰富的信息&#xff0c;包括网格…...

windows系统安装驱动、cuda和cudnn

一、首先在自己的电脑里安装了nvidia的独立显卡 显卡的查找方式&#xff1a; CtrlShiftEsc打开任务管理器&#xff0c;点击性能&#xff0c;点击GPU 0查看显卡型号&#xff0c;如下图所示&#xff1a; 只要电脑中有nvidia的独立显卡&#xff0c;就可以暗转显卡驱动、cuda和cu…...

嵌入式开发--STM32软件和硬件CRC的使用--续篇

本文是《嵌入式开发–STM32软件和硬件CRC的使用》的续篇&#xff0c;又踩到一个坑&#xff0c;发出来让大家避一下坑。 按照G0系列的设置&#xff0c;得出错误的结果 前文对应的是STM32G0系列&#xff0c;今天在用STM32G4系列时&#xff0c;按照前文的设置&#xff0c;用硬件…...

2.深入剖析 Rust+Axum 类型安全路由系统

摘要 详细解读 RustAxum 路由系统的关键设计原理&#xff0c;涵盖基于 Rust 类型系统的路由匹配机制、动态路径参数与正则表达式验证以及嵌套路由与模块化组织等多种特性。 一、引言 在现代 Web 开发中&#xff0c;路由系统是构建 Web 应用的核心组件之一&#xff0c;它负责…...

c# 委托和事件的区别及联系,Action<T1,T2>与Func<T1,T2>的区别

定义与本质‌ ‌委托‌: 委托是一种类型&#xff0c;用于存储对方法的引用。它允许将方法作为参数传递、存储和调用。委托可以绑定一个或多个方法&#xff0c;并通过和-操作符动态添加或移除方法。‌ ‌事件‌: 事件是基于委托的封装&#xff0c;提供了一种发布/订阅机制。事件…...

【Python入门】文件读取全攻略:5种常用格式(csv/excel/word/ppt/pdf)一键搞定 | 附完整代码示例

大家好&#xff0c;我是唐叔&#xff01;今天给大家带来一篇Python文件读取的终极指南。无论是数据分析、办公自动化还是爬虫开发&#xff0c;文件读取都是Python程序员必须掌握的核心技能。本文将详细介绍Python处理5大常用文件格式的方法&#xff0c;包含完整可运行的代码示例…...

【Git】git的简单使用

文章目录 1. 基础概念2. 简单使用2.1 git配置2.1.1 git的配置文件2.1.2 .gitignore文件 2.2 创建仓库2.2.1 创建本地仓库2.2.2 github创建远程仓库step1&#xff1a;github新建一个代码仓step2&#xff1a;创建密钥远程仓库相关指令2.2.3 本地仓库 关联 远程仓库 2.3 分支2.3.1…...

WebSocket 实现数据实时推送原理

WebSocket 实现数据实时推送的核心机制在于其全双工通信能力和持久的连接特性。以下是其工作原理的详细步骤&#xff1a; 1. 握手阶段&#xff08;HTTP 升级协议&#xff09; 客户端发起请求&#xff1a;通过发送一个带有特殊头部的 HTTP 请求&#xff0c;请求协议升级。 GET …...

LeetCode 2919 使数组变美的最小增量运算数

动态规划解题&#xff1a;最小操作次数使数组变为美丽数组 问题描述 给定一个下标从0开始、长度为n的整数数组nums和一个整数k。你可以对数组中的任意一个元素进行加1操作&#xff0c;操作次数不限。如果数组中任意长度大于或等于3的子数组的最大值都大于或等于k&#xff0c;…...

[Web 安全] Web 信息收集 —— 信息收集流程

&#x1f31f; 想系统化学习 Web 渗透&#xff1f;看看这个&#xff1a;[Web 安全] Web 安全攻防 学习手册 提示&#xff1a;本章不涉及任何具体信息收集技术&#xff0c;仅仅是讲解收集这些信息我能干啥&#xff0c;以及如何才能比较全面的收集信息。 0x01&#xff1a;信息收…...

内部聊天软件,BeeWorks-安全的企业内部通讯软件

企业在享受数据便利的同时&#xff0c;如何保障企业数据安全已经成为无法回避的重要课题。BeeWorks作为一款专为企业设计的内部通讯软件&#xff0c;通过全链路的安全能力升维&#xff0c;为企业提供了一个安全、高效、便捷的沟通协作平台&#xff0c;全面保障企业数据安全。 …...

线程安全学习

1 什么是线程 线程是cpu调度的最小单位&#xff0c;在Linux 下 实现线程的方式为轻量级进程&#xff0c;复用进程的结构体&#xff0c;使用clone函数创建 2 线程安全 所谓线程安全&#xff0c;更确切的应该描述为内存安全 #include <stdio.h> #include <pthread.h…...

应用篇02-镜头标定(上)

本节主要介绍相机的标定方法&#xff0c;包括其内、外参数的求解&#xff0c;以及如何使用HALCON标定助手实现标定。 计算机视觉——相机标定(Camera Calibration)_摄像机标定-CSDN博客 1. 原理 本节介绍与相机标定相关的理论知识&#xff0c;不一定全&#xff0c;可以参考相…...

【UE5 C++】“ProceduralMeshComponent”的使用记录

效果 如下所示&#xff0c;通过“ProceduralMeshComponent”创建了一个自定义形状的Mesh&#xff0c;并且该Mesh包含碰撞信息&#xff0c;然后2s后更新Mesh形状。 步骤 1. 在“xxx.Build.cs”中引入“ProceduralMeshComponent”模块 2. 新建一个Actor类&#xff0c;这里命名为…...

解决PIP 安装出错ERROR: cp310-cp310-manylinux_2_28_x86_64.whl is not a supported wheel

ERROR: torch-2.8.0.dev20250325cu128-cp310-cp310-manylinux_2_28_x86_64.whl is not a supported wheel on this platform. 可以 pip debug --verbose | grep manylinux | grep cp310 WARNING: This command is only meant for debugging. Do not use this with automation f…...

通过helm在k8s中安装mysql 8.0.37

使用 Helm 在 Kubernetes 中安装 MySQL 8.0.37 是一个相对简单的过程。以下是详细步骤&#xff1a; 下载helm包 #添加 Helm 仓库 helm repo add bitnami https://charts.bitnami.com/bitnami#搜索mysql helm search repo mysql --versions NAME CHAR…...

【暴力求解】1534. 统计好三元组

1534. 统计好三元组 - 力扣&#xff08;LeetCode&#xff09; 给你一个整数数组 arr &#xff0c;以及 a、b 、c 三个整数。请你统计其中好三元组的数量。 如果三元组 (arr[i], arr[j], arr[k]) 满足下列全部条件&#xff0c;则认为它是一个 好三元组 。 0 < i < j &l…...

前端页面效果收集

文章目录 数字雨元素融化动画电子签名共享屏幕 数字雨 <canvas id"matrix"></canvas> <script>const canvas document.getElementById(matrix);const ctx canvas.getContext(2d);canvas.width window.innerWidth;canvas.height window.innerH…...

(leetcode算法题)309. 买卖股票的最佳时机含冷冻期

按照题目要求&#xff0c;研究对象是最后一天结束后获得的最大利润 那么就可以把问题拆分成 第 1 天结束后获得的最大利润&#xff0c; 第 2 天结束后获得的最大利润&#xff0c; 第 i 天结束后获得的最大利润&#xff0c; 由于规则中强调不能同时参与多笔交易&#xff0c…...

Chrome漏洞可窃取数据并获得未经授权的访问权限

在发现两个关键漏洞后,谷歌发布了Chrome浏览器的紧急安全更新。这些漏洞可能允许攻击者窃取敏感数据并未经授权访问用户系统。 这些缺陷被识别为CVE-2025-3619和CVE-2025-3620,在Windows和Mac的135.0.7049.95/.96之前影响Chrome版本,影响Linux的135.0.7049.95/.96。该更新将在…...

.net core 项目快速接入Coze智能体-开箱即用-全局说明

目录 一、Coze智能体的核心价值 二、开箱即用-效果如下 三 流程与交互设计 为什么要分析意图&#xff0c;而不是全部交由AI处理。 四 接入前的准备工作 五&#xff1a;代码实现----字节Coze 签署 JWT和获取Token .net core 项目快速接入Coze智能体-开箱即用 .net core快…...

风丘年度活动:2025年横滨汽车工程展览会

| 展会简介&#xff1a; 2025年横滨汽车工程展览会&#xff0c;是由日本汽车工程师学会&#xff08;JSAE&#xff09;精心主办的一场行业盛会。预计届时将汇聚超550家参展商&#xff0c;设置1300个展位&#xff0c;展览面积超过20000平方米。展会受众广泛&#xff0c;面向汽车…...

Redis线上操作最佳实践有哪些?

大家好&#xff0c;我是锋哥。今天分享关于【Redis线上操作最佳实践有哪些?】面试题。希望对大家有帮助&#xff1b; Redis线上操作最佳实践有哪些? 1000道 互联网大厂Java工程师 精选面试题-Java资源分享网 在使用 Redis 时&#xff0c;尤其是在生产环境中&#xff0c;合理…...