解析 C# Dictionary 代码
entries用于存储当前每个节点的数据,其中四个字段分别表示:
hashCode:key对应的hash值next:处理hash冲突,可以理解为是一个链表结构,邻接表key:存储的keyvalue:存储的value
buckets存储桶,用于存储hash映射
设计的巧妙之处在这个桶buckets,每个hashCode取模(当前size最近的大于size的素数),这里用素数是为了减少hash冲突的频率,key取hash再取模=index,就放入存储桶中,buckets[index]记录的是当前key对应的第一个entries的下标位置,entries不管是增加还是删除,会优先连续的放在数组中,举例删了index[2]的数据,这个时候加入一个数据,会无脑放在这个index[2]的位置,同理[.next]也维护了删除的单向链表的数据,所有删掉的index下标数据,也是存在链中等待新的数据插入,这里是反向链表获取index,所以可以理解为最早清理的index,最晚被用到。
具体可以看看代码
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.Contracts;
using System.Runtime.Serialization;
using System.Security.Permissions;
using System.Text;namespace DictionaryTest
{public static class MHashHelpers{public const int MaxPrimeArrayLength = 0x7FEFFFFD;public static readonly int[] primes = {3, 7, 11, 17, 23, 29, 37, 47, 59, 71, 89, 107, 131, 163, 197, 239, 293, 353, 431, 521, 631, 761, 919,1103, 1327, 1597, 1931, 2333, 2801, 3371, 4049, 4861, 5839, 7013, 8419, 10103, 12143, 14591,17519, 21023, 25229, 30293, 36353, 43627, 52361, 62851, 75431, 90523, 108631, 130363, 156437,187751, 225307, 270371, 324449, 389357, 467237, 560689, 672827, 807403, 968897, 1162687, 1395263,1674319, 2009191, 2411033, 2893249, 3471899, 4166287, 4999559, 5999471, 7199369};public static bool IsPrime(int candidate){if ((candidate & 1) != 0){int limit = (int)Math.Sqrt(candidate);for (int divisor = 3; divisor <= limit; divisor += 2){if ((candidate % divisor) == 0)return false;}return true;}return (candidate == 2);}public static int GetPrime(int min){for (int i = 0; i < primes.Length; i++){int prime = primes[i];if (prime >= min) return prime;}for (int i = (min | 1); i < Int32.MaxValue; i += 2){if (IsPrime(i) && ((i - 1) % 101 != 0))return i;}return min;}public static int ExpandPrime(int oldSize){int newSize = 2 * oldSize;// Allow the hashtables to grow to maximum possible size (~2G elements) before encoutering capacity overflow.// Note that this check works even when _items.Length overflowed thanks to the (uint) castif ((uint)newSize > MaxPrimeArrayLength && MaxPrimeArrayLength > oldSize){Contract.Assert(MaxPrimeArrayLength == GetPrime(MaxPrimeArrayLength), "Invalid MaxPrimeArrayLength");return MaxPrimeArrayLength;}return GetPrime(newSize);}}public class MDictionary<TKey, TValue>{private struct Entry{public int hashCode; // 存储 Key 对应的 hashCodepublic int next; // 指向上一个存储的对象的indexpublic TKey key; // 存储的 Keypublic TValue value; // 存储的 Value}private IEqualityComparer<TKey> comparer;private int[] buckets; // 定长的存储桶private Entry[] entries; // 定长的存储元素的数组private int count; // 当前元素个数private int freeList; // 删掉元素之后,还空余的位置,最新的一个,根据entries链表指向上一个private int freeCount; // 空余的个数public MDictionary() : this(0) { }public MDictionary(int capacity){if (capacity > 0){Initialize(capacity);}this.comparer = EqualityComparer<TKey>.Default;}private void Initialize(int capacity){int size = MHashHelpers.GetPrime(capacity);buckets = new int[size];entries = new Entry[size];for (int i = 0; i < buckets.Length; i++){buckets[i] = -1;entries[i].hashCode = -1;entries[i].next = -1;entries[i].key = default(TKey);entries[i].value = default(TValue);}count = 0;freeList = -1;freeCount = 0;}private void Resize(){Console.WriteLine($"[Resize]");int newSize = MHashHelpers.ExpandPrime(count);int[] newBuckets = new int[newSize];for (int i = 0; i < newBuckets.Length; i++) newBuckets[i] = -1;Entry[] newEntries = new Entry[newSize];Array.Copy(entries, 0, newEntries, 0, count);for (int i = 0; i < count; i++){if (newEntries[i].hashCode != -1){newEntries[i].hashCode = (comparer.GetHashCode(newEntries[i].key) & 0x7FFFFFFF);}}for (int i = 0; i < count; i++){if (newEntries[i].hashCode != -1){int bucket = newEntries[i].hashCode % newSize;newEntries[i].next = newBuckets[bucket];newBuckets[bucket] = i;}}buckets = newBuckets;entries = newEntries;PrintAll();}public void Insert(TKey key, TValue value){Console.WriteLine($"[Insert] {key} {value}");int hashCode = comparer.GetHashCode(key) & 0x7FFFFFFF;int targetBucket = hashCode % buckets.Length;for (int i = buckets[targetBucket]; i != -1; i = entries[i].next){if (entries[i].hashCode == hashCode && comparer.Equals(entries[i].key, key)){entries[i].value = value;PrintAll();return;}}int index;if (freeCount > 0){index = freeList;freeList = entries[index].next;freeCount--;}else{if (count == buckets.Length){Resize();targetBucket = hashCode % buckets.Length;}index = count;count++;}entries[index].hashCode = hashCode;entries[index].next = buckets[targetBucket];entries[index].key = key;entries[index].value = value;buckets[targetBucket] = index;PrintAll();}public bool Remove(TKey key){Console.WriteLine($"[Remove] {key}");int hashCode = comparer.GetHashCode(key) & 0x7FFFFFFF;int bucket = hashCode % buckets.Length;int last = -1;for (int i = buckets[bucket]; i != -1; last = i, i = entries[i].next){if (comparer.Equals(key, entries[i].key) && entries[i].hashCode == hashCode){if (last == -1){buckets[bucket] = entries[i].next;}else{entries[last].next = entries[i].next;}entries[i].hashCode = -1;entries[i].next = -1;entries[i].key = default(TKey);entries[i].value = default(TValue);freeList = i;freeCount++;PrintAll();return true;}}PrintAll();return false;}public bool TryGetValue(TKey key, out TValue value){Console.WriteLine($"[TryGetValue] {key}");int hashCode = comparer.GetHashCode(key) & 0x7FFFFFFF;int targetBucket = hashCode % buckets.Length;for (int i = buckets[targetBucket]; i != -1; i = entries[i].next){if (comparer.Equals(key, entries[i].key) && entries[i].hashCode == hashCode){value = entries[i].value;return true;}}value = default(TValue);return false;}public void Clear(){Console.WriteLine($"[Clear]");if (count > 0){for (int i = 0; i < buckets.Length; i++) buckets[i] = -1;Array.Clear(entries, 0, count);count = 0;freeList = -1;freeCount = 0;}PrintAll();}public void PrintAll(){Console.WriteLine($"[PrintAll]");StringBuilder bucketInfo = new StringBuilder();StringBuilder entriesInfo = new StringBuilder();for (int i = 0; i < buckets.Length; i++){bucketInfo.Append($"{i} [{buckets[i]}]").Append("\n");}Console.WriteLine("buckets: " + buckets.Length);Console.Write(bucketInfo);for (int i = 0; i < entries.Length; i++){entriesInfo.Append($"{i} [{entries[i].hashCode}={entries[i].next}={entries[i].key}={entries[i].value}]").Append("\n");}Console.WriteLine("entries: " + entries.Length);Console.Write(entriesInfo);}}class Program{static void Main(string[] args){Console.WriteLine("Hello World!");MDictionary<int, string> dict = new MDictionary<int, string>(2);dict.Clear();dict.Insert(0, "000");dict.Insert(1, "111");dict.Remove(0);dict.Insert(2, "222");dict.Insert(3, "333");dict.Insert(4, "444");dict.Remove(2);dict.Insert(5, "555");dict.Insert(6, "666");dict.Remove(1);dict.Insert(7, "777");dict.Insert(14, "1414");dict.Insert(21, "2121");dict.Remove(14);dict.Insert(28, "2828");dict.Insert(35, "3535");dict.Insert(42, "4242");dict.Insert(55, "5555");dict.Remove(21);}}
}
一些参考:
https://zhuanlan.zhihu.com/p/96633352
Reference Source
https://www.cnblogs.com/pengze0902/p/17830689.html
相关文章:
解析 C# Dictionary 代码
entries用于存储当前每个节点的数据,其中四个字段分别表示: hashCode:key对应的hash值next:处理hash冲突,可以理解为是一个链表结构,邻接表key:存储的keyvalue:存储的value bucket…...
如何利用人工智能提升工作效率
在当今这个信息爆炸的时代,我们每天都被大量的工作任务所困扰。然而,随着人工智能技术的不断发展,我们可以通过一些智能工具来提升我们的工作效率。在这篇文章中,我将分享一些关于如何利用人工智能提升工作效率的建议。 首先&…...
Linux驱动开发—Linux内核定时器概念和使用详解,实现基于定时器的字符驱动
文章目录 内核定时器概念在Linux驱动模块中使用定时器软定时器(Soft Timers)jiffies 含义高精度定时器(High Resolution Timers) 实现倒计时字符设备驱动 内核定时器概念 在 Linux 内核中,定时器是用来管理和调度延迟…...
mysql数据库:数据库,表和列的基本概念
mysql:数据库,表和列的基本概念以及导入和导出文件 数据库的概念和用途 数据库是一个有组织的数据集合,它们被存储在计算机上以便于管理和访问。数据库的主要目的是为了存储和管理数据,同时使数据能够被高效地访问、检索和更新。数…...
Nextjs 使用 graphql,并且接入多个节点
写在前面 随着区块链技术的流行,也促进了 subgraph 工具的兴起。那么如何在前端接入 graphql 节点就成了关键,其接入方式既存在与 restful 接口相类似的方式,也有其独特接入风格。本文将介绍如何接入 graphql 以及如何应对多个 graphql 节点…...
小结——知识注入
所谓知识注入,其实不该脱离于LLM的基础工作原理,然后空谈抽象概念。 知识,也就是你问他问题,他能输出正确的回答,这只是一个简单的输出token的过程。输出得准了,就是知识,输出不准了,…...
科普文:微服务之Spring Cloud Alibaba组件Nacos一致性协议Distro+Raft概叙
一、概要 Nacos是阿里开放的一款中间件,它主要提供三种功能:持久化节点注册,非持久化节点注册和配置管理。 二、一致性协议 - AP/CP Nacos不是纯粹的AP服务,也不是纯粹的CP服务,而是两者同时支持。 这要从服务注册…...
python合并音视频-通过ffmpeg合并音视频
🌈所属专栏:【python】✨作者主页: Mr.Zwq✔️个人简介:一个正在努力学技术的Python领域创作者,擅长爬虫,逆向,全栈方向,专注基础和实战分享,欢迎咨询! 您的…...
Yolov8添加ConvNetV1和V2模块
Yolov8添加ConvNet模块 1 ConvNet系列相关内容 (1)2022 论文地址:A ConvNet for the 2020s Code Link 如下图所示,精度、效率、尺寸都很不错。 论文的摘要如下: 视觉识别的“咆哮的 20 年代”始于视觉注意力 &…...
十个常见的 Python 脚本 (详细介绍 + 代码举例)
1. 批量重命名文件 介绍: 该脚本用于批量重命名指定目录下的文件,例如将所有 ".txt" 文件重命名为 ".md" 文件。 import osdef batch_rename(directory, old_ext, new_ext):"""批量重命名文件扩展名。Args:directory: 要处理…...
【C语言】详解feof函数和ferror函数
文章目录 前言1. feof1.1 feof函数原型1.2 正确利用函数特性读写文件1.2.1 针对文本文件1.2.2 针对二进制文件 1.3 feof函数的原理1.4 feof函数实例演示 2. ferror2.1 ferror函数原型 前言 或许我们曾在网络上看过有关于feof函数,都说这个函数是检查文件是否已经读…...
ValueListenableBuilder 和 addListener 在 ChangeNotifier的区别
1、前言 ValueListenableBuilder 和 addListener 在 ChangeNotifier 中有不同的用途和用法,适用于不同的场景。它们的主要区别在于它们如何监听和响应状态变化,以及它们的用法和特性。 2、ValueListenableBuilder用法 ValueListenableBuilder 是一个 …...
ScriptEcho:AI赋能的前端代码生成神器
ScriptEcho:AI赋能的前端代码生成神器 在前端开发中,如果你总是觉得写代码太费时费力,那么 ScriptEcho 将成为你的救星。这个 AI 代码生成平台不仅能帮你省下大量时间,还能让你轻松愉快地写出生产级代码。本文将带你了解 ScriptEc…...
TypeError: ‘float’ object is not iterable 深度解析
TypeError: ‘float’ object is not iterable 深度解析与实战指南 在Python编程中,TypeError: float object is not iterable是一个常见的错误,通常发生在尝试对浮点数(float)进行迭代操作时。这个错误表明代码中存在类型使用不…...
灵茶八题 - 子序列 +w+
灵茶八题 - 子序列 w 题目描述 给你一个长为 n n n 的数组 a a a,输出它的所有非空子序列的元素和的元素和。 例如 a [ 1 , 2 , 3 ] a[1,2,3] a[1,2,3] 有七个非空子序列 [ 1 ] , [ 2 ] , [ 3 ] , [ 1 , 2 ] , [ 1 , 3 ] , [ 2 , 3 ] , [ 1 , 2 , 3 ] [1],[…...
为什么美元债务会越来越多?
美元债务规模持续膨胀,其背后原因复杂多样,可归结为以下几个主要因素: 财政赤字和刺激政策是导致美元债务增加的重要原因。美国政府长期面临财政赤字问题,支出远超收入,为弥补这一缺口,政府不得不大量发行…...
二维凸包算法 Julia实现
问题描述:给定平面上 n n n 个点的集合 Q Q Q,求其子集 P P P 构成 Q Q Q 的凸包,即 ∀ p ∈ Q , ∃ p 0 , p 1 , p 2 ∈ P \forall p \in Q, \exist p_0, p_1, p_2 \in P ∀p∈Q,∃p0,p1,p2∈P 使得点 p p p 在以点 p 0 , p 1 …...
python dash框架
Dash 是一个用于创建数据分析型 web 应用的 Python 框架。它由 Plotly 团队开发,并且可以用来构建交互式的 web 应用程序,这些应用能够包含图表、表格、地图等多种数据可视化组件。 Dash 的特点: 易于使用:Dash 使用 Python 语法…...
2.外部中断(EXTI)
理论 NVIC:嵌套向量中断控制器(解释教程) 外部通用中断线(EXTI0~EXTI15):每个GPIO设置成中断模式,与中断控制器连接的线 外部中断触发方式 上升沿触发、下降沿触发、双边沿触发 外部中断触发函数 在stm32f1xx_it.c文件…...
Python | SyntaxError: invalid syntax 深度解析
Python | SyntaxError: invalid syntax 深度解析 在Python编程中,SyntaxError: invalid syntax是一个常见的错误,它表明Python解释器在尝试解析代码时遇到了语法问题。这个错误通常是由于代码中存在拼写错误、缺少符号(如括号、冒号或逗号&a…...
Tracecat:AI原生安全自动化平台架构解析与实战指南
1. 项目概述:一个为安全团队打造的AI原生自动化平台如果你是一名安全工程师、SOC分析师或者运维负责人,每天被海量的告警、重复的排查任务和繁琐的工单流程搞得焦头烂额,那么今天聊的这个开源项目Tracecat,可能会成为你工具箱里的…...
Python类型配置最后的黄金窗口期:CPython 3.13即将强制增强类型元数据,错过将影响未来5年架构演进
更多请点击: https://intelliparadigm.com 第一章:Python类型配置的战略意义与时代背景 在现代软件工程演进中,Python 类型配置已从可选辅助机制跃升为系统可靠性、团队协作效率与长期可维护性的核心基础设施。随着大型项目(如Py…...
10分钟快速掌握nerf_pl:从零开始的神经辐射场训练终极指南
10分钟快速掌握nerf_pl:从零开始的神经辐射场训练终极指南 【免费下载链接】nerf_pl 项目地址: https://gitcode.com/gh_mirrors/ne/nerf_pl nerf_pl是一个基于PyTorch Lightning实现的神经辐射场(NeRF)开源项目,它提供了…...
109 【自适应天线与相控阵技术】基于近场扫描的偏移相位中心天线测量
目录 编辑 1. 引言 2. DPCA基本原理与几何推导 2.1 运动平台杂波谱展宽 2.2 双相位中心空间重合条件 2.3 静止杂波对消与运动目标保留 2.4 等效单基地雷达位置 3. 阵列结构与相位中心控制 3.1 企业馈电与双通道接收 3.2 重叠孔径与分裂孔径 3.3 阵列互耦与保护带 …...
使用 Python 快速接入 Taotoken 并调用多模型 API 的完整步骤
使用 Python 快速接入 Taotoken 并调用多模型 API 的完整步骤 1. 准备工作 在开始编写代码之前,您需要完成几个简单的准备工作。首先确保您拥有一个 Taotoken 账户,并在控制台中创建了 API Key。登录 Taotoken 平台后,可以在「API 密钥管理…...
告别CNN!用BERT的思路搞定加密流量分类:PERT实战教程与代码解析
告别CNN!用BERT的思路搞定加密流量分类:PERT实战教程与代码解析 在网络安全领域,加密流量分类一直是个棘手的问题。传统的基于CNN的方法虽然取得了一定成效,但面对日益复杂的加密技术,其局限性逐渐显现。本文将带你探…...
鼠标连点器:游戏玩家的得力助手
在玩某些游戏的时候,我们经常需要反复点击鼠标,时间长了手指会很酸痛。 而且有些场景需要非常快速的连点,手动很难达到理想的速度。 这时候鼠标连点器就派上用场了,能帮我们完成这些重复性的点击工作。 今天我们要介绍的这款鼠标连…...
如何高效使用KMS智能激活脚本:Windows和Office激活完整指南
如何高效使用KMS智能激活脚本:Windows和Office激活完整指南 【免费下载链接】KMS_VL_ALL_AIO Smart Activation Script 项目地址: https://gitcode.com/gh_mirrors/km/KMS_VL_ALL_AIO KMS_VL_ALL_AIO是一款功能强大的智能激活脚本,专为Windows和O…...
FeHelper:前端开发者的效率神器,30+工具集成与实战技巧
1. 项目概述:一个前端工程师的“瑞士军刀”如果你和我一样,是个每天和浏览器、代码、API打交道的前端开发者,那你一定经历过这些场景:调试接口时,拿到一串压缩得面目全非的JSON,得找个在线工具格式化&#…...
避开这些坑!QMT量化策略实盘逆回购的3个常见问题与调试技巧
避开这些坑!QMT量化策略实盘逆回购的3个常见问题与调试技巧 在量化交易的世界里,逆回购策略因其相对稳定的收益和较低的风险,成为许多初入市场的量化交易者的首选。然而,当策略从回测环境迁移到实盘运行时,往往会遇到各…...
