Loading [MathJax]/jax/input/TeX/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >原 NET中dictionary的一个小坑

原 NET中dictionary的一个小坑

作者头像
魂祭心
发布于 2018-05-17 09:38:34
发布于 2018-05-17 09:38:34
68100
代码可运行
举报
文章被收录于专栏:魂祭心魂祭心
运行总次数:0
代码可运行

问题描述:前段时间做个东西,打算在dictionary上按顺序扫描,不复合key条件的元素移动到末尾,然后减少计数,计算下 一个元素。目的就是一遍扫描实现对key的分组处理,减少遍历次数。然而程序表现与预想中不一致,表现

                 出来 就 是移除再插入的顺序和处理前的顺序一致。

                复现后的场景:

                  移除顺序对添加的顺序有影响。

原因:

        产生这个问题的原因在于Dictionary内部的实现机制,简单来说dictionary中维护了一个bucket数组和entry数组,

       前者用于key的hash定位,后者负责数据存储,一个freelist维护了一个当前空位入口。至于细节请参考这篇博客

http://www.cnblogs.com/1-2-3/archive/2010/10/25/generic-dictionary-source-part2.html

        查看源码中移除代码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
        public bool Remove(TKey key) {           
         if(key == null) {               
          ThrowHelper.ThrowArgumentNullException(ExceptionArgument.key);
            } 
            if (buckets != null) {           
                 int hashCode = comparer.GetHashCode(key) & 0x7FFFFFFF;          
                 int bucket = hashCode % buckets.Length;       
                 int last = -1;               
                 for (int i = buckets[bucket]; i >= 0; last = i, i = entries[i].next)
                 {                    
                 if (entries[i].hashCode == hashCode && comparer.Equals(entries[i].key, key)) {                            if (last < 0) {                           
                              buckets[bucket] = entries[i].next;
                        }                      
                          else {                     
                              entries[last].next = entries[i].next;
                        }                       
                         entries[i].hashCode = -1;              
                         entries[i].next = freeList;         
                         entries[i].key = default(TKey);                        
                         entries[i].value = default(TValue);                        
                         freeList = i;                        
                         freeCount++;                        
                         version++;                        
                         return true;
                    }
                }
            }            
            return false;
        }

         字典每次移除都会清空当前entry并将entry并入空闲索引链接中,指针指向上一个空位,当前entry作为索引链接的入口。

Add的方法源码截取:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
            int index;            
            if (freeCount > 0) 
            {                
            index = freeList;               
            freeList = entries[index].next;  
            freeCount--;
            }            
            else {                
            if (count == entries.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;            
            version++;

代码中首先需要获取到空闲位置,index=freeList中获取了最近移除的位置。后边对这个entry进行赋值了。

在看dictionary中获取顺序的代码截取:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
  while ((uint)index < (uint)dictionary.count) 
  {                    
      if (dictionary.entries[index].hashCode >= 0) 
      {                        
           current = new KeyValuePair<TKey, TValue>(dictionary.entries[index].key, dictionary.entries[index].value);                        
            index++;                        
            return true;
       }
   }

位置是在enrties块上面进行顺序读取的。

分析:

       结合三段代码分析,当移除一个键的时候,当前块会成为空白块的入口,再次添加键的时候会重新占据这个位置。这可以解释我碰到的死循环问题。

     图解中可以看出这种情况的原因。结果符合预期的。dictionary中的顺序是不可信任的,如果一定要使用,需要考虑全面些。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
C#创建安全的字典(Dictionary)存储结构
彭泽0902
2018/01/04
2.5K0
浅析C# Dictionary实现原理
本篇文章配图以及文字其实整理出来很久了,但是由于各种各样的原因推迟到现在才发出来,还有之前立Flag的《多线程编程》的笔记也都已经写好了,只是说还比较糙,需要找个时间整理一下才能和大家见面。
全球技术精选
2021/08/20
2370
Dictionary源码解析及实现原理(C#)
Dictionary 又称C#中的哈希表,是一个Collection(集合)类型,可以通过Key/Value(键值对)的形式来存放数据;该类最大的优点就是它查找元素的时间复杂度接近O(1),实际项目中常被用来做一些数据的本地缓存,提升整体效率。
用户11236293
2024/08/14
1450
.net源码分析 – Dictionary<TKey, TValue>
接上篇:.net源码分析 – List<T> Dictionary<TKey, TValue>源码地址:https://github.com/dotnet/corefx/blob/master/src
用户1147588
2018/01/04
1.8K0
.net源码分析 – Dictionary<TKey, TValue>
算法和数据结构: 十一 哈希表
在前面的系列文章中,依次介绍了基于无序列表的顺序查找,基于有序数组的二分查找,平衡查找树,以及红黑树,下图是他们在平均以及最差情况下的时间复杂度:
yaphetsfang
2020/07/30
1K0
算法和数据结构: 十一 哈希表
你真的了解字典吗(dictionary)?
半年前,我参加我现在所在公司的面试,面试官给了一道题,说有一个Y形的链表,知道起始节点,找出交叉节点.
码农阿宇
2019/02/13
6810
DotNet Dictionary 实现简介
本来笔者对DotNet的Hashtable及Dictionary认识一直集中在使用上,一个直接用object 一个可以用泛型,以前也只大概看过Hashtable的实现。最近查MSDN时发现有建议开发者使用Dictionary代替Hashtable的描述,出于好奇测试了Hashtable及Dictionary读写性能,发现无论读还是写Dictionary都大幅领先Hashtable,然后就花时间整理了Dictionary操作逻辑试图找到这种性能提升的原因(最后会发现实现上的差异带来的性能明显提升也算的上是理所当然)。下文实际是介绍的Dictionary的实现(调试中使用的源是corefx 3.1),其中穿插着对比了Hashtable的实现逻辑。
lulianqi
2022/03/11
3560
DotNet Dictionary 实现简介
.net源码分析 - ConcurrentDictionary<TKey, TValue>
List源码分析 Dictionary源码分析 ConcurrentDictionary源码分析 继上篇Dictionary源码分析,上篇讲过的在这里不会再重复 ConcurrentDiction
用户1147588
2018/01/04
1.3K0
ConsurrentDictionary并发字典知多少?
背景 在上一篇文章你真的了解字典吗?一文中我介绍了Hash Function和字典的工作的基本原理. 有网友在文章底部评论,说我的Remove和Add方法没有考虑线程安全问题. https://d
码农阿宇
2019/03/20
8930
ConsurrentDictionary并发字典知多少?
C#集合类型大揭秘
集合是.NET FCL(Framework Class Library)的重要组成部分,我们平常撸C#代码时免不了和集合打交道,FCL提供了丰富易用的集合类型,给我们撸码提供了极大的便利。正是因为这种与生俱来的便利性,使得我们对集合既熟悉又陌生。很多同学可能一直还是停留在使用的层面上,那么今天我们一起来深入学习一下C#语言中的各种集合。
撸码那些事
2018/06/15
1.2K0
C#集合类型大揭秘
我大意了,没有闪。
(1) 栗子1重新new赋值难道不是修改了原字典对象newDict吗?foreach字典为什么不报InvalidOperation异常?
有态度的马甲
2023/09/05
2520
我大意了,没有闪。
【算法与数据结构】--高级算法和数据结构--哈希表和集合
哈希表(Hash Table)是一种常用的数据结构,其核心原理是将数据存储在数组中,并使用哈希函数来映射数据的键(Key)到数组中的特定位置,这个位置通常被称为“哈希桶”或“槽位”。哈希表允许快速的数据查找、插入和删除操作,通常在平均情况下,这些操作的时间复杂度为O(1)。以下是哈希表的基本原理:
喵叔
2023/10/17
5380
C# Dictionary 字典
说明 必须包含名空间System.Collection.Generic Dictionary里面的每一个元素都是一个键值对(由二个元素组成:键和值) 键必须是唯一的,而值不需要唯一的 键和值都可以是任何类型(比如:string, int, 自定义类型,等等) 通过一个键读取一个值的时间是接近O(1) 键值对之间的偏序可以不定义
zls365
2021/03/16
1.2K0
.NET中的泛型集合
近对集合相关的命名空间比较感兴趣,以前也就用下List, Dictionary<Tkey, TValue>之类,总之,比较小白。点开N多博客,MSDN,StackOverflow,没找到令我完全满意的答案,本打算自己总结下写出来,工作量好大的感觉……直到昨晚随意翻到看了一些又放下的《深入理解C#》-附录B部分,高兴地简直要叫出来——“这总结真是太绝了,好书不愧是好书”。真是“踏破铁鞋无觅处,得来全不费工夫”,最好的资源就在眼下,而自己居然浑然不知。或许只有深入技术细节的时候,才能认识到经典为什么经典吧!言归正传,本博客主要是对《深入理解C#》-附录B的摘录,并加了些标注。
郑子铭
2023/08/29
2850
.NET中的泛型集合
数据结构基础温故-6.查找(下):哈希表
哈希(散列)技术既是一种存储方法,也是一种查找方法。然而它与线性表、树、图等结构不同的是,前面几种结构,数据元素之间都存在某种逻辑关系,可以用连线图示表示出来,而哈希技术的记录之间不存在什么逻辑关系,它只与关键字有关联。因此,哈希主要是面向查找的存储结构。哈希技术最适合的求解问题是查找与给定值相等的记录。
Edison Zhou
2018/08/20
6270
数据结构基础温故-6.查找(下):哈希表
C#中HashTable、Dictionary、ConcurrentDictionary区别
HashTable表示键/值对的集合。在.NET Framework中,Hashtable是System.Collections命名空间提供的一个容器,用于处理和表现类似key-value的键值对,其中key通常可用来快速查找,同时key是区分大小写;value用于存储对应于key的值。Hashtable中key-value键值对均为object类型,所以Hashtable可以支持任何类型的keyvalue键值对,任何非 null 对象都可以用作键或值。
用户9127601
2021/11/01
8630
.NET 排序 Array.Sort<T> 实现分析
System.Array.Sort<T> 是.NET内置的排序方法, 灵活且高效, 大家都学过一些排序算法,比如冒泡排序,插入排序,堆排序等,不过你知道这个方法背后使用了什么排序算法吗? 先说结果,
全球技术精选
2021/10/09
6590
.NET 排序 Array.Sort<T> 实现分析
CSharp中字典(Dictionary)的使用
Dictionary<TKey, TValue> 是泛型类型,其中 TKey 表示键的类型,TValue 表示值的类型。
码客说
2024/05/30
4090
C#集合类型大揭秘
集合是.NET FCL(Framework Class Library)的重要组成部分,我们平常撸C#代码时免不了和集合打交道,FCL提供了丰富易用的集合类型,给我们撸码提供了极大的便利。正是因为这种
撸码那些事
2018/06/21
1.6K0
面试时被问到Flutter/Dart的HashMap怎么办?
相信不少同学在面试的时候有被问到关于HashMap的问题,特别是Java/Android程序员,HashMap几乎是必然会被提及的。因为这里面可以挖掘的点实在是太多了。关于Java的HashMap面经在网上可以说是随处可见了。自然而然,随着Flutter的火爆,后面大家也可能在面试中被问到关于Flutter/Dart的HashMap相关问题。与其到时候一问三不知,不如现在就来了解一下Flutter/Dart的HashMap吧。
HowHardCanItBe
2021/05/21
1.2K0
相关推荐
C#创建安全的字典(Dictionary)存储结构
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验