【Unity主程手记(摘录)】第一章(二) - Dictory 底层源码剖析

文章详细剖析了Dictory的数据结构,包括使用哈希函数处理Key-Value映射及解决冲突的方法。添加数据时,Dictory会根据需要动态扩容,通常选择质数作为新的容量。删除数据并不真正释放内存,而是将对应位置设为默认值。此外,由于Dictionary非线程安全,多线程环境下需手动加锁。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

第一章(二) - Dictory 底层源码剖析

提示:个人学习总结,如有错误,敬请指正。



一.Dictory

1.底层数据结构

Key和Value用一个Hash函数来建立映射关系,处理Hash哈希冲突的方法在数据结构中会有详细讲解

Dictionary 是以数组为底层数据结构的类。当我们实例化 new Dictionary() 后,内部的数组是0个数组的状态。与 List 组件一样,Dictionary 也是需要扩容的,会随着元素数量的增加而不断扩容。


2.Add - 添加数据

Add 接口就是 Insert 的代理

返回一个size需要的最小质数值,首次定义为3每次扩容两倍-即3->7->17->37(每次都是质数)

int size = HashHelpers.GetPrime(capacity);

对Hash地址执行余数操作确保再数组长度方范围内不溢出。

    int hashCode = comparer.GetHashCode(key) & 0x7FFFFFFF;
    int targetBucket = hashCode % buckets.Length;

指定数组单元格内的链表元素做遍历操作,找出空出来的位置将值填入;
若数组大小不够则需要再次扩容


3.Remove- 删除数据

  • 用哈希函数 comparer.GetHashCode 再除余后得到范围内的地址索引,再做余操作确定地址落在数组范围内,从哈希索引地址开始,查找冲突的元素的Key是否与需要移除的Key值相同,相同则进行移除操作并退出。
  • Remove并没有对内存进行删减,而是将对应hash位置置为默认值,这是为了减少内存的频繁操作。

4.其他接口

  • ContainsKey :
    • 与之前类似,FindEntry使用和前面相同的方式查找key的hash位置,然后从链表里匹配元素,成功返回该索引地址。
  • TryGetValue :
    • 与 ContainsKey 同样,他调用的也是FindEntry的接口,来获取Key对应的Value值。

5.Hash函数的创建过程

哈希冲突的拉链法贯穿了整个底层数据结构。因此哈希函数是关键了,哈希函数的好坏直接决定了效率高低。

    if (t == typeof(byte)) {
        return (EqualityComparer<T>)(object)(new ByteEqualityComparer());
    }
    // If T implements IEquatable<T> return a GenericEqualityComparer<T>
    if (typeof(IEquatable<T>).IsAssignableFrom(t)) {
        return (EqualityComparer<T>)RuntimeTypeHandle.CreateInstanceForAnotherGenericParameter((RuntimeType)typeof(GenericEqualityComparer<int>), t);
    }
    // If T is a Nullable<U> where U implements IEquatable<U> return a NullableEqualityComparer<U>
    if (t.IsGenericType && t.GetGenericTypeDefinition() == typeof(Nullable<>)) {
        RuntimeType u = (RuntimeType)t.GetGenericArguments()[0];
        if (typeof(IEquatable<>).MakeGenericType(u).IsAssignableFrom(u)) {
            return (EqualityComparer<T>)RuntimeTypeHandle.CreateInstanceForAnotherGenericParameter((RuntimeType)typeof(NullableEqualityComparer<int>), u);
        }
    }

对于数字类型,他们实现了IEquatable接口,直接使用GenericEqualityComparer获得hash函数。否则如果实现了Nullable接口,则调用NullableEquality-Comparer(),如果不是以上两种情况则调用ObjectEqualityComparer。


6.线程安全

和List一样,Dictionary并不是线程安全的
Hashtable在多线程读/写中是线程安全的,而Dictionary不是。如果要在多个线程中共享Dictionary的读/写操作,就要自己写lock,以保证线程安全。


7.总结

  • 从效率上看,同List一样最好在 实例化对象时,即 new 时尽量确定大致数量会更加高效,另外用数值方式做Key比用类实例方式作为Key值更加高效率
  • 从内存操作上看,大小以3->7->17->37->….的速度,每次增加2倍多的顺序进行,删除时,并不缩减内存
  • 如果想在多线程中,共享 Dictionary 则需要进行我们自己进行lock操作。
  • 减少Dictionary的冗余访问
	if(Dic.Contains(key))
	{
		var result = Dic[key];
	}

	//改写成

	value result ;
	if(Dic.TryGetValue(key,out result))
	{
		//
	}

附录

主程手记

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值