Unity Dictionary的底层实现及部分方法的底层实现

目录

底层

方法

字典的优缺点

底层

在 Unity 中,Dictionary(字典)的底层实现是使用哈希表(hash table)。哈希表是一种常用的数据结构,它通过将键(key)映射到对应的值(value)来进行快速查找和插入操作。

Dictionary 内部使用了一个数组来存储元素,每个元素包含一个键值对。具体而言,它使用一个哈希函数将键转换为一个索引值,然后将该索引值作为数组的下标来存储对应的键值对。

当我们添加键值对时,Dictionary 首先会计算键的哈希码(hash code),然后根据哈希码找到对应的索引位置。如果该位置已经存在其他键值对,可能发生冲突,即多个键的哈希码相同。在这种情况下,Dictionary 使用链表(linked list)来解决冲突,即在同一个索引位置上存储一个链表,其中包含多个哈希码相同但不同的键值对。

在查找时,Dictionary 根据要查找的键的哈希码,定位到对应的索引位置,并遍历链表来比较键的值,找到匹配的键值对。

需要注意的是,为了保持高效性能,Dictionary 在内部会自动调整数组的大小,以确保具有适当的容量来容纳键值对。因此,在添加大量键值对时,可能会引发内部数组的重新分配和复制操作。

总结以来Dictionary 的底层实现包含以下要点:

  • 哈希函数:用于将键映射到内部数组的索引位置。Unity 使用了一些高效的哈希函数算法来确保散列值分布均匀,减少冲突的可能性。
  • 冲突解决方法:当多个键映射到了同一个索引位置时,Unity 使用链表或红黑树等数据结构来存储具有相同索引的键值对。这样可以在冲突较少的情况下保持较好的性能。
  • 动态调整大小:与 List 类似,Dictionary 也具有自动扩容的能力。当 Dictionary 的负载因子(Load Factor)超过阈值时,会重新分配更大的内部数组,并将旧的键值对重新哈希到新的位置上。负载因子是指存储在字典中的元素数量与内部数组大小的比率。
  • 快速查找:由于使用了哈希表的优势,Dictionary 具有快速的查找和插入操作。平均情况下,查找和插入操作的时间复杂度为 O(1)。

方法

在 Unity 中,Dictionary 类型提供了一系列方法来操作和管理键值对。下面是一些常用的 Dictionary 方法的详细说明:

  1. Add(key, value):该方法首先使用键的哈希码通过哈希函数计算出一个索引位置。然后,根据这个索引,在内部数组中找到对应的桶(bucket)。如果桶为空,则将键值对添加到桶中;如果桶不为空,就需要处理冲突。在处理冲突时,Dictionary 使用链表来维护具有相同哈希码的键值对。它会遍历链表,比较键的值,找到匹配的键值对,或者将新的键值对添加到链表的末尾。

  2. Remove(key):该方法首先使用键的哈希码通过哈希函数计算出索引位置。然后,在该索引位置上的链表中遍历,找到匹配的键值对,并将其从链表中移除。

  3. TryGetValue(key, out value):该方法首先使用键的哈希码通过哈希函数计算出索引位置。然后,在该索引位置上的链表中遍历,比较键的值,找到匹配的键值对,并将对应的值存储在提供的变量中。

  4. ContainsKey(key):该方法首先使用键的哈希码通过哈希函数计算出索引位置。然后,在该索引位置上的链表中遍历,比较键的值,找到匹配的键值对。

  5. ContainsValue(value):该方法通过遍历所有桶和链表来查找具有指定值的键值对。

  6. Clear():该方法将内部数组初始化为空,直接清空了所有的键值对。

  7. Keys:该方法返回一个包含字典中所有键的集合。这个集合实际上是在底层数组上构建的,通过遍历所有桶和链表来收集键。返回结果类型为 Dictionary<TKey, TValue>.KeyCollection

  8. Values:该方法返回一个包含字典中所有值的集合。这个集合实际上是在底层数组上构建的,通过遍历所有桶和链表来收集值。返回结果类型为 Dictionary<TKey, TValue>.ValueCollection

  9. Count:该方法返回字典中键值对的数量。在底层实现中,它是通过遍历所有桶和链表,并累加每个链表的节点数来计算的。

  10. ryAdd(key, value):该方法尝试向字典中添加键值对。它首先使用哈希函数计算键的哈希码,并通过索引找到对应的桶。如果桶为空,则将键值对直接添加到桶中。如果桶不为空,则需要进行链表遍历,检查是否存在相同的键。如果存在相同的键,则返回 false 表示添加失败;如果不存在相同的键,则将键值对添加到链表的末尾,并返回 true 表示成功添加。

  11. GetValueOrDefault(key, defaultValue):该方法首先使用哈希函数计算键的哈希码,并通过索引找到对应的桶。然后,在该索引位置上的链表中遍历,比较键的值,找到匹配的键值对,并返回其值;如果没有找到匹配的键值对,则返回提供的默认值。

  12. Remove(key, out value):该方法首先使用哈希函数计算键的哈希码,并通过索引找到对应的桶。然后,在该索引位置上的链表中遍历,比较键的值,找到匹配的键值对,并将其从链表中移除。同时,将被移除的值存储在提供的变量中,并返回 true 表示成功移除;如果没有找到匹配的键值对,则返回 false。

  13. CopyTo(array, arrayIndex):该方法用于将字典中的键值对复制到一个二维数组中的指定索引处。它通过遍历所有桶和链表来收集键值对,并将其复制到指定的数组位置。

  14. TrimExcess():该方法用于调整字典的内部数组的大小以适应实际元素的数量。当添加或删除大量元素后,内部数组可能会有过多的空闲空间,这个方法可以根据实际元素数量进行缩小,减少内存占用。

  15. GetOrAdd(key, valueFactory):该方法首先使用哈希函数计算键的哈希码,并通过索引找到对应的桶。然后,在该索引位置上的链表中遍历,比较键的值,找到匹配的键值对,并返回其值。如果没有找到匹配的键值对,则使用提供的委托函数创建新值,并将新的键值对添加到链表的末尾,并返回新创建的值。

  16. GetEnumerator():该方法返回一个枚举器(Enumerator),用于循环遍历字典中的键值对。枚举器会依次遍历所有的桶和链表,返回一个包含键值对的结构体(KeyValuePair)。

字典的优缺点

Unity 中的 Dictionary 类型具有以下优点和缺点:

优点:

  1. 高效的查找操作:Dictionary 使用哈希表来实现,能够以常数时间复杂度(O(1))进行查找操作,即使在具有大量元素的情况下,查找速度也非常快。

  2. 灵活的存储能力:Dictionary 可以存储各种类型的键值对,这使得它非常适用于需要动态添加和管理数据的情况。它可以根据需要自动调整大小,以适应元素的添加和删除操作。

  3. 方便的键值对操作:通过 Dictionary 提供的方法,可以方便地添加、删除、获取和检查键值对。这使得我们可以轻松地对字典中的数据进行增删改查操作,并灵活地处理字典中的键和值。

  4. 支持泛型:Dictionary 是一个泛型类,可以根据需要指定键和值的类型,从而提高类型安全性和代码可读性。

缺点:

  1. 内存消耗较大:由于使用了哈希表,Dictionary 在内存中会占用一定的空间。尤其是当 Dictionary 包含大量元素或存在大量哈希冲突时,可能会导致额外的内存消耗。

  2. 不保持顺序:Dictionary 中的键值对是无序存储的,即添加和遍历元素的顺序不一定与插入顺序相同。如果需要按照特定的顺序来访问键值对,可能需要使用其他数据结构。

  3. 哈希冲突的影响:当多个键具有相同哈希码时,会导致哈希冲突,使得查找效率下降。尽管 Dictionary 使用链表来解决冲突,但在存在大量冲突的情况下,链表可能变得较长,影响查找和删除操作的性能。

  4. 不适用于有序需求:如果需要根据键的顺序进行排序或按照范围进行查询,Dictionary 并不是最佳选择。在这种情况下,应该考虑使用 SortedDictionary 或其他支持有序操作的数据结构。

总结起来,Unity 的 Dictionary 类型底层使用哈希表实现,通过计算键的哈希码,将键值对映射到数组索引来实现高效的查找和插入操作。其底层实现考虑了哈希冲突的情况,并使用链表解决冲突,以保证高效的性能和灵活的存储能力。

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Unity协程的底层实现是基于C#的迭代器实现的。在C#中,使用yield关键字可以将方法转换为迭代器,通过迭代器可以实现协程的效果。Unity中的协程也是基于这个原理实现的。 如果要自己实现一个协程,可以按照以下步骤进行: 1. 定义一个委托,用于表示协程的执行体。 ```csharp public delegate IEnumerator CoroutineDelegate(); ``` 2. 定义一个协程类,保存协程的执行体和当前执行状态。 ```csharp public class Coroutine { private CoroutineDelegate m_CoroutineDelegate; private IEnumerator m_Enumerator; private bool m_IsDone; public bool IsDone { get { return m_IsDone; } } public Coroutine(CoroutineDelegate coroutineDelegate) { m_CoroutineDelegate = coroutineDelegate; m_Enumerator = m_CoroutineDelegate(); m_IsDone = false; } public void Update() { if (m_Enumerator != null && !m_IsDone) { if (!m_Enumerator.MoveNext()) { m_IsDone = true; } } } } ``` 3. 在需要使用协程的地方,创建一个协程对象并添加到一个协程管理器中。 ```csharp public class CoroutineManager : MonoBehaviour { private static CoroutineManager m_Instance; private List<Coroutine> m_Coroutines = new List<Coroutine>(); public static CoroutineManager Instance { get { if (m_Instance == null) { m_Instance = new GameObject("CoroutineManager").AddComponent<CoroutineManager>(); } return m_Instance; } } private void Update() { for (int i = m_Coroutines.Count - 1; i >= 0; i--) { Coroutine coroutine = m_Coroutines[i]; coroutine.Update(); if (coroutine.IsDone) { m_Coroutines.RemoveAt(i); } } } public Coroutine StartCoroutine(CoroutineDelegate coroutineDelegate) { Coroutine coroutine = new Coroutine(coroutineDelegate); m_Coroutines.Add(coroutine); return coroutine; } } ``` 4. 在协程中使用yield关键字来实现挂起和恢复。 ```csharp private IEnumerator MyCoroutine() { Debug.Log("Start Coroutine"); yield return null; Debug.Log("Wait One Frame"); yield return new WaitForSeconds(1.0f); Debug.Log("Wait One Second"); yield return new WaitForEndOfFrame(); Debug.Log("Wait End Of Frame"); } ``` 以上就是一个简单的协程实现。注意,实际应用中还需要考虑协程的取消、异常处理等问题,需要根据具体需求进行扩展。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值