前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >原 NET中dictionary的一个小坑

原 NET中dictionary的一个小坑

作者头像
魂祭心
发布2018-05-17 17:38:34
6250
发布2018-05-17 17:38:34
举报
文章被收录于专栏:魂祭心魂祭心

问题描述:前段时间做个东西,打算在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
复制
        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
复制
            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
复制
  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 删除。

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

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
数据保险箱
数据保险箱(Cloud Data Coffer Service,CDCS)为您提供更高安全系数的企业核心数据存储服务。您可以通过自定义过期天数的方法删除数据,避免误删带来的损害,还可以将数据跨地域存储,防止一些不可抗因素导致的数据丢失。数据保险箱支持通过控制台、API 等多样化方式快速简单接入,实现海量数据的存储管理。您可以使用数据保险箱对文件数据进行上传、下载,最终实现数据的安全存储和提取。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档