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

C# Dictionary的实现原理

在 C# 中,Dictionary<TKey, TValue> 是一个基于哈希表(Hash Table)实现的键值对集合。它提供了高效的插入、删除和查找操作,平均时间复杂度接近 O(1)。下面是 Dictionary 的核心实现原理:


1. Dictionary 的核心数据结构

C# 的 Dictionary<TKey, TValue> 主要由以下几个部分组成:

  • 数组(buckets):存储哈希桶(Bucket)的索引。
  • 数组(entries):存储键值对(哈希表中的实际数据)。
  • 哈希函数(GetHashCode):将键映射到哈希桶。
  • 碰撞解决(拉链法):采用 链地址法(Chaining with Linked List)。
  • 负载因子(Load Factor):决定何时扩容,默认为 0.75。

数据结构

private int[] buckets;         // 哈希桶数组,存储 entry 的索引
private Entry[] entries;       // 存储实际的键值对
private int count;             // 当前存储的元素个数
private int freeList;          // 指向空闲 entry(删除后形成的空位)
private int freeCount;         // 空闲 entry 的数量private struct Entry
{public int hashCode;       // 计算出的哈希值public int next;           // 指向下一个冲突的元素(-1 表示无冲突)public TKey key;           // 键public TValue value;       // 值
}

2. Dictionary 的主要操作

(1)添加元素

当调用 dictionary.Add(key, value) 时,Dictionary 执行以下步骤:

  1. 计算哈希值:调用 key.GetHashCode() 计算哈希值 hashCode

  2. 计算索引:对哈希值取模,index = hashCode % buckets.Length,确定应该存放的桶。

  3. 检查冲突:

    • 如果该桶为空,则直接存储。此时count字段记录字典中元素个数。令bucket[index] = count,然后将内容存储到Entry[count]中,随后count++。
    • 如果发生哈希冲突,则使用链地址法存储,即将 entries 中的 next 指向旧的 Entry,形成链表。
  4. 扩容(如有必要):

    • 如果 count > capacity * 0.75,则触发扩容,通常是 2 倍扩容

    public void Add(TKey key, TValue value)

    {

     int hashCode = key.GetHashCode() & 0x7FFFFFFF;  // 计算哈希值int index = hashCode % buckets.Length;  // 计算桶的索引// 处理哈希冲突:如果桶为空,直接插入if (buckets[index] == -1){buckets[index] = count;  // 将当前元素插入桶数组entries[count] = new Entry { hashCode = hashCode, key = key, value = value, next = -1 	};count++;}else{// 发生哈希冲突,遍历链表int current = buckets[index];while (current >= 0){Entry entry = entries[current];if (entry.hashCode == hashCode && EqualityComparer<TKey>.Default.Equals(entry.key, key)){entries[current].value = value;  // 如果找到相同的键,更新值return;}current = entry.next;  // 查找下一个节点}// 如果没有找到,插入新的元素到链表头部entries[count] = new Entry { hashCode = hashCode, key = key, value = value, next = buckets[index] };buckets[index] = count;  // 更新桶的索引count++;}// 扩容检查if (count > buckets.Length * 0.75){Resize();  // 扩容}
    

    }

(2)查找元素

当调用 dictionary[key]TryGetValue(key, out value) 时:

  1. 计算哈希值hashCode = key.GetHashCode()

  2. 计算索引index = hashCode % buckets.Length

  3. 遍历链表

    • 如果桶中第一个元素匹配,则直接返回。
    • 如果有哈希冲突(next != -1),遍历 entries 直到找到 key

(3)删除元素

当调用 dictionary.Remove(key) 时:

  1. 计算哈希值,找到哈希桶索引。
  2. 遍历链表,找到匹配的 Entry
  3. 更新链表指针:
    • 如果是第一个元素,则更新 buckets 指向 next 位置。
    • 如果在链表中,则调整 next 以跳过该元素。
  4. 回收 entry:
    • entry 记录到 freeList,方便后续使用。

(4)扩容机制

count >= capacity * 0.75 时,Dictionary 会进行 2 倍扩容

  1. 创建更大的 buckets 和 entries 数组(通常是 2 倍大小)。
  2. 重新计算索引,重新分配 bucketsentries
  3. 重新插入所有 Entry,因为 hashCode % newCapacity 结果不同,所有键值对需要重新计算索引。

3. Dictionary 的碰撞处理

C# 的 Dictionary 采用 链地址法 处理哈希冲突:

  • entries 形成链表,每个 Entry 记录 next 指向同一个桶的下一个 Entry
  • 遍历链表时,检查 hashCodekey 是否匹配。

示例假设 buckets 长度为 5,并插入以下键值对:

dict.Add(1, "Apple");
dict.Add(6, "Banana"); // 6 % 5 == 1,与 1 发生哈希冲突

哈希桶结构如下:

buckets: [ -1,  0 -> 1, -1, -1, -1 ]   // index 1 存储链表 (1 → 6)
entries: 0: { hashCode=1, key=1, value="Apple", next=1 }1: { hashCode=6, key=6, value="Banana", next=-1 }

查找 dict[6] 时:

  1. 计算 index = 6 % 5 = 1
  2. 遍历链表:
    • entries[0] (key=1) 不匹配,继续遍历 next=1
    • entries[1] (key=6) 匹配,返回 "Banana"

4. Dictionary 的优缺点

优点

  • 查找快:平均时间复杂度 O(1)
  • 插入删除快:平均时间复杂度 O(1)
  • 自动扩容:避免手动管理大小。

缺点

  • 内存占用大:数组 + 链表可能浪费额外空间。
  • 哈希碰撞影响性能:冲突越多,查找速度降至 O(n)
  • 不保证顺序Dictionary 无序存储键值对。

5. Dictionary 的替代方案

数据结构适用场景
SortedDictionary<TKey, TValue>需要按键排序(基于 红黑树,O(log n))
SortedList<TKey, TValue>适用于小数据量,查找快但插入慢
HashSet<T>仅存储键,不存储值
ConcurrentDictionary<TKey, TValue>多线程安全字典

相关文章:

C# Dictionary的实现原理

在 C# 中&#xff0c;Dictionary<TKey, TValue> 是一个基于哈希表&#xff08;Hash Table&#xff09;实现的键值对集合。它提供了高效的插入、删除和查找操作&#xff0c;平均时间复杂度接近 O(1)。下面是 Dictionary 的核心实现原理&#xff1a; 1. Dictionary 的核心数…...

学习笔记-人脸识别相关编程基础

通过编程实现人脸识别功能&#xff0c;需要掌握一定的技术基础&#xff0c;包括编程语言、图像处理、机器学习以及相关的库和框架&#xff1a; 1. 编程语言 Python&#xff1a;Python 是实现人脸识别最常用的语言之一&#xff0c;因为它有大量的库和框架支持&#xff0c;如 Op…...

BUU37 [DASCTF X GFCTF 2024|四月开启第一局]web1234【代码审计/序列化/RCE】

Hint1&#xff1a;本题的 flag 不在环境变量中 Hint2&#xff1a;session_start&#xff08;&#xff09;&#xff0c;注意链子挖掘 题目&#xff1a; 扫描出来www.zip class.php <?phpclass Admin{public $Config;public function __construct($Config){//安全获取基…...

(五)Spring Boot学习——spring security +jwt使用(前后端分离模式)

一定要熟悉spring security原理和jwt无状态原理&#xff0c;理解了才知道代码作用。 在 Spring Security JWT 认证流程中&#xff0c;通常的做法是&#xff1a; 用户提交用户名和密码Spring Security 认证管理器 (AuthenticationManager) 进行认证如果认证成功&#xff0c;生…...

Java中使用EasyExcel

Java中使用EasyExcel 文章目录 Java中使用EasyExcel一&#xff1a;EasyExcel介绍1.1、核心函数导入数据导出数据 1.2、项目实际应用导入数据导出数据 1.3、相关注解ExcelProperty作用示例 二&#xff1a;EasyExcel使用2.1、导入功能2.2、导出功能 三&#xff1a;EasyExcel完整代…...

前沿科技改变生活新趋势

纳米技术在电子设备制造中的应用越来越广泛。这种技术能够帮助制造更小、更快、更耐用的电子产品。 举个例子&#xff0c;手机的处理器是其核心部件。随着纳米技术的进步&#xff0c;现在的处理器比以前小得多&#xff0c;但功能却更强。这样不仅让手机变得更轻薄&#xff0c;…...

不到一个月,SQLite 3.49.0来了

距离 SQLite 3.48.0 发布不到一个月&#xff0c;SQLite 开发团队于 2025 年 2 月 6 日发布了 SQLite 3.49.0 版本。这更新速度的确让人感动&#xff0c;那么这个版本又有哪些更新呢&#xff1f; 查询优化器 新版本改进了自动索引&#xff08;query-time index&#xff09;优化…...

Android车机DIY开发之软件篇(十四)编译i.mx8mplus官方kernel

1.下载 下载地址 2.安装依赖 sudo apt-get update sudo apt-get install build-essential git libncurses5-dev libssl-dev bc sudo apt-get install gcc-aarch64-linux-gnu export CROSS_COMPILEaarch64-linux-gnu- 3.配置 make ARCHarm64 defconfig 4.编译 make ARCHa…...

Mac上搭建宝塔环境并部署PHP项目

安装Docker Desktop》搭建Centos版本的宝塔环境》部署PHP项目 1. 下载Docker for mac 软件&#xff1a;https://www.docker.com/ 或使用终端命令&#xff1a;brew install --cask --appdir/Applications docker 2. 使用命令安装宝塔环境的centos7系统&#xff1a; docker pul…...

3.3.3 VO-O语法- 语法算子(二)

循环遍历 由于VO语言是面向数据集的&#xff0c;其所有隐含的语义中都已经带有了遍历并计算的数据逻辑。因此&#xff0c;VO语言只提供了一种支持循环语法的算子--Loop算子。 Loop算子 Loop算子是一个容器算子&#xff0c;其可以实现对其内部子流程的循环迭代运行。但Loop算…...

安装 Ollama 需要哪些步骤?(windows+mac+linux+二进制+Docker)

安装 Ollama 的步骤根据操作系统不同会有所差异,以下是针对不同操作系统的详细安装指南: Windows 系统 下载安装包:访问 Ollama 官方下载页面,下载适用于 Windows 的安装程序 OllamaSetup.exe。运行安装程序:双击下载的安装包,按照提示完成安装。默认安装路径为 C:\User…...

HCIA项目实践--静态路由的综合实验

八 静态路由综合实验 &#xff08;1&#xff09;划分网段 # 192.168.1.0 24#分析&#xff1a;每个路由器存在两个环回接口&#xff0c;可以把两个环回接口分配一个环回地址&#xff0c;所以是四个环回&#xff0c;一个骨干&#xff0c;这样分配&#xff0c;不会出现路由黑洞#19…...

Electron视图进程和主进程通讯

快速创建基于vue的electron项目&#xff1a;quick-start/create-electron - npm 视图线程也就index.html是无法直接访问这个api的&#xff08;如果没有开启视图层访问nodejs的功能&#xff0c;现在几乎没法直接开启&#xff0c;开启了一堆警告提示&#xff09; 所以需要通过r…...

Vript-Hard——一个基于高分辨率和详细字幕的视频理解算法

一、概述 多模态学习的最新进展促进了对视频理解和生成模型的研究。随之而来的是&#xff0c;对高分辨率视频和详细说明所建立的高质量数据集的需求激增。然而&#xff0c;由于时间因素的影响&#xff0c;视频与文本的配对不像图像那样容易。准备视频和文本配对是一项困难得多…...

react脚手架搭建react项目使用scss

1.create-react-app 创建的项目&#xff0c;webpack配置默认是隐藏的 &#xff0c;如果要查看 或修改用npm run eject命令,因为create-react-app脚手架默认已经配置了scss、sass所以不用改webpack配置。如果用less 就需要自己添加配置 2.如果直接使用scss的文件会直接报错&…...

Vue.js 状态管理库Pinia

Pinia Pinia &#xff1a;Vue.js 状态管理库Pinia持久化插件-persist Pinia &#xff1a;Vue.js 状态管理库 Pinia 是 Vue 的专属状态管理库&#xff0c;它允许你跨组件或页面共享状态。 要使用Pinia &#xff0c;先要安装npm install pinia在main.js中导入Pinia 并使用 示例…...

【Stable Diffusion部署至GNU/Linux】安装流程

以下是安装Stable Diffusion的步骤,以Ubuntu 22.04 LTS为例子。 显卡与计算架构介绍 CUDA是NVIDIA GPU的专用并行计算架构 技术层级说明CUDA Toolkit提供GPU编译器(nvcc)、数学库(cuBLAS)等开发工具cuDNN深度神经网络加速库(需单独下载)GPU驱动包含CUDA Driver(需与CUDA …...

【C/C++算法】从浅到深学习---滑动窗口(图文兼备 + 源码详解)

绪论&#xff1a;冲击蓝桥杯一起加油&#xff01;&#xff01; 每日激励&#xff1a;“不设限和自我肯定的心态&#xff1a;I can do all things。 — Stephen Curry” 绪论​&#xff1a; 本章是算法训练的第二章----滑动窗口&#xff0c;它的本质是双指针算法的衍生所以我将…...

计算机毕业设计SpringBoot+Vue.js房源推荐系统 房价预测 房源大数据分析可视化(源码+文档+运行视频+讲解视频)

温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 作者简介&#xff1a;Java领…...

开源机器人+具身智能 解决方案+AI

开源机器人、具身智能(Embodied Intelligence)以及AI技术的结合,可以为机器人领域带来全新的解决方案。以下是这一结合的可能方向和具体方案: 1. 开源机器人平台 开源机器人平台为开发者提供了灵活的基础架构,可以在此基础上结合具身智能和AI技术。以下是一些常用的开源机…...

vscode里如何用git

打开vs终端执行如下&#xff1a; 1 初始化 Git 仓库&#xff08;如果尚未初始化&#xff09; git init 2 添加文件到 Git 仓库 git add . 3 使用 git commit 命令来提交你的更改。确保在提交时加上一个有用的消息。 git commit -m "备注信息" 4 …...

postgresql|数据库|只读用户的创建和删除(备忘)

CREATE USER read_only WITH PASSWORD 密码 -- 连接到xxx数据库 \c xxx -- 授予对xxx数据库的只读权限 GRANT CONNECT ON DATABASE xxx TO read_only; GRANT USAGE ON SCHEMA public TO read_only; GRANT SELECT ON ALL TABLES IN SCHEMA public TO read_only; GRANT EXECUTE O…...

生成 Git SSH 证书

&#x1f511; 1. ​​生成 SSH 密钥对​​ 在终端&#xff08;Windows 使用 Git Bash&#xff0c;Mac/Linux 使用 Terminal&#xff09;执行命令&#xff1a; ssh-keygen -t rsa -b 4096 -C "your_emailexample.com" ​​参数说明​​&#xff1a; -t rsa&#x…...

相机Camera日志分析之三十一:高通Camx HAL十种流程基础分析关键字汇总(后续持续更新中)

【关注我,后续持续新增专题博文,谢谢!!!】 上一篇我们讲了:有对最普通的场景进行各个日志注释讲解,但相机场景太多,日志差异也巨大。后面将展示各种场景下的日志。 通过notepad++打开场景下的日志,通过下列分类关键字搜索,即可清晰的分析不同场景的相机运行流程差异…...

关于 WASM:1. WASM 基础原理

一、WASM 简介 1.1 WebAssembly 是什么&#xff1f; WebAssembly&#xff08;WASM&#xff09; 是一种能在现代浏览器中高效运行的二进制指令格式&#xff0c;它不是传统的编程语言&#xff0c;而是一种 低级字节码格式&#xff0c;可由高级语言&#xff08;如 C、C、Rust&am…...

c#开发AI模型对话

AI模型 前面已经介绍了一般AI模型本地部署&#xff0c;直接调用现成的模型数据。这里主要讲述讲接口集成到我们自己的程序中使用方式。 微软提供了ML.NET来开发和使用AI模型&#xff0c;但是目前国内可能使用不多&#xff0c;至少实践例子很少看见。开发训练模型就不介绍了&am…...

Java编程之桥接模式

定义 桥接模式&#xff08;Bridge Pattern&#xff09;属于结构型设计模式&#xff0c;它的核心意图是将抽象部分与实现部分分离&#xff0c;使它们可以独立地变化。这种模式通过组合关系来替代继承关系&#xff0c;从而降低了抽象和实现这两个可变维度之间的耦合度。 用例子…...

【SSH疑难排查】轻松解决新版OpenSSH连接旧服务器的“no matching...“系列算法协商失败问题

【SSH疑难排查】轻松解决新版OpenSSH连接旧服务器的"no matching..."系列算法协商失败问题 摘要&#xff1a; 近期&#xff0c;在使用较新版本的OpenSSH客户端连接老旧SSH服务器时&#xff0c;会遇到 "no matching key exchange method found"​, "n…...

【LeetCode】算法详解#6 ---除自身以外数组的乘积

1.题目介绍 给定一个整数数组 nums&#xff0c;返回 数组 answer &#xff0c;其中 answer[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积 。 题目数据 保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内。 请 不要使用除法&#xff0c;且在 O…...

Kubernetes 节点自动伸缩(Cluster Autoscaler)原理与实践

在 Kubernetes 集群中&#xff0c;如何在保障应用高可用的同时有效地管理资源&#xff0c;一直是运维人员和开发者关注的重点。随着微服务架构的普及&#xff0c;集群内各个服务的负载波动日趋明显&#xff0c;传统的手动扩缩容方式已无法满足实时性和弹性需求。 Cluster Auto…...