• 设为首页
  • 点击收藏
  • 手机版
    手机扫一扫访问
    迪恩网络手机版
  • 关注官方公众号
    微信扫一扫关注
    公众号

C#Dictionary的底层实现解析

原作者: [db:作者] 来自: [db:来源] 收藏 邀请

前言:很多文章描述过于复杂故整理之。

1C# Dictionary设计思想:

1.1 数据结构

  • 创建大小为size的数组entries(用来存放字典元素--以下称:entry)
  • 创建桶buckets数组记录entry的index(大小和entries保持一致)
  • entry结构体:hash、nextIndex(下个entry的index)、key、value

1.2 数据维护(逻辑)

  • 添加entry时,计算该entry hashCode落在桶的位置(规则是hashCode对桶求余)
  • 当出现落在的位置(桶)里已经有别的“index”了,那么这些冲突的index根据先来后到建立单链表(即链地址法--开放地址法、链地址法、建立溢出区、再哈希法4大方法之一) 可以发现,最理想的状态是,每个entry一一对应的落在buckets数组上是最划算的(查询等消耗最低)。
  • 当entries数组长度不够用时扩容,buckets也需要同步扩容。
  • 记录freeCount,freeCount == count(entries的)-Count(字典的)
  • 字典有个版本字段,增删改都会导致+1。(意义:比如遍历的过程是否版本被修改--增删)(freeList;//记录最近一次remove的实体的idx)

1.3 链地址图示--桶冲突

1.4 部分源码例子

      private void Resize(int newSize, bool forceNewHashCodes) {
            Contract.Assert(newSize >= entries.Length);
            //创建新的桶数组
            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);
            //强制重新计算hashCode
            if(forceNewHashCodes) {
                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 >= 0) {
                    //当hashCode大于-1(该元素有值),赋值桶的值
                    int bucket = newEntries[i].hashCode % newSize;
                    newEntries[i].next = newBuckets[bucket];
                    newBuckets[bucket] = i;
                }
            }
            buckets = newBuckets;
            entries = newEntries;
        }

Find操作

       private int FindEntry(TKey key) {
            ...
           int hashCode = comparer.GetHashCode(key) & 0x7FFFFFFF;
           //对应的桶拿到该桶位置最近一次的实体的idx, 遍历桶位置的单链表,如果对应的key和参数key相等,则return
           for (int i = buckets[hashCode % buckets.Length]; i >= 0; i = entries[i].next) {
               if (entries[i].hashCode == hashCode && comparer.Equals(entries[i].key, key)) return i;
            }
            ...
            return -1;
        }

建议查看源码深入理解

扩展:
https://www.cnblogs.com/InCerry/p/10325290.html


鲜花

握手

雷人

路过

鸡蛋
该文章已有0人参与评论

请发表评论

全部评论

专题导读
上一篇:
C#需要在项目程序生成前后执行相关的事件发布时间:2022-07-10
下一篇:
C#通过反射获取对象发布时间:2022-07-10
热门推荐
阅读排行榜

扫描微信二维码

查看手机版网站

随时了解更新最新资讯

139-2527-9053

在线客服(服务时间 9:00~18:00)

在线QQ客服
地址:深圳市南山区西丽大学城创智工业园
电邮:jeky_zhao#qq.com
移动电话:139-2527-9053

Powered by 互联科技 X3.4© 2001-2213 极客世界.|Sitemap