38.数据结构选择优化

38.性能优化-CPU-脚本-数据结构选择


38.1 知识点

数据结构选择

数据结构选择是指在 Unity 脚本中用合适的数据结构来装载数据。常用的 ListDictionary 各有优劣,应按场景选用。

List<T>:

  • 查找时间复杂度 O(n)(按索引访问为 O(1))。
  • 存储结构连续,遍历时缓存友好。

Dictionary<TKey, TValue>:

  • 查找时间复杂度 O(1)(按 key)。
  • 存储结构不连续,依赖哈希表。

选择建议:

  1. 经常遍历、中间插入较少:用 List,顺序访问快、内存局部性好。
  2. 经常按键查找:用 Dictionary,避免线性查找。

示例:

using System.Collections.Generic;

// 需要频繁遍历、顺序处理时用 List
List<Enemy> enemyList = new List<Enemy>();
foreach (Enemy enemy in enemyList)
{
    enemy.UpdateLogic();
}

// 需要按 ID 等键快速查找时用 Dictionary
Dictionary<int, Player> playerDictionary = new Dictionary<int, Player>();
if (playerDictionary.TryGetValue(playerId, out Player player))
{
    player.ApplyDamage(amount);
}

更多数据结构选择

除 List 和 Dictionary 外,下列结构在特定场景下更合适。

HashSet<T>

  • 时间复杂度:查找 / 添加 / 删除均为 O(1),内部为哈希表。
  • 使用场景:只关心“是否存在”、不需要顺序、也不需要键值对,例如当前激活的敌人集合、已处理对象集合。
using System.Collections.Generic;

// 只关心“是否已包含”,不关心顺序
HashSet<GameObject> activeEnemySet = new HashSet<GameObject>();
activeEnemySet.Add(enemy);
if (activeEnemySet.Contains(enemy)) { }
activeEnemySet.Remove(enemy);

Queue<T>

  • 时间复杂度:入队、出队 O(1)
  • 使用场景:按顺序处理(先进先出),例如对象池中存放可复用对象。
using System.Collections.Generic;

// 对象池:从队头取、用完后从队尾放回
Queue<GameObject> poolQueue = new Queue<GameObject>();
poolQueue.Enqueue(instance);
GameObject next = poolQueue.Dequeue();

Stack<T>

  • 时间复杂度:入栈、出栈 O(1)
  • 使用场景:先进后出,例如行为回退、状态回溯。
using System.Collections.Generic;

// 状态/行为回退
Stack<GameState> stateStack = new Stack<GameState>();
stateStack.Push(currentState);
GameState previous = stateStack.Pop();

LinkedList<T>

  • 时间复杂度:在已知节点处插入、删除 O(1),按值查找 O(n)。内存不连续,缓存命中率低,不推荐频繁整体遍历。
  • 使用场景:需要频繁在中间插入、删除,且不依赖整体遍历性能时。
using System.Collections.Generic;

// 频繁在中间插入/删除,且已有节点引用时
LinkedList<Node> nodeList = new LinkedList<Node>();
LinkedListNode<Node> node = nodeList.Find(target);
if (node != null)
    nodeList.Remove(node);

SortedDictionary<TKey, TValue>、SortedList<TKey, TValue>

  • 特点:按 Key 有序;插入、删除、查找 O(log n),比 Dictionary 慢,但顺序稳定。
  • 使用场景:需要按 Key 顺序遍历键值对,例如按时间调度的事件。
using System.Collections.Generic;

// 按时间键顺序处理事件
SortedDictionary<float, Action> scheduledEvents = new SortedDictionary<float, Action>();
foreach (KeyValuePair<float, Action> pair in scheduledEvents)
{
    if (pair.Key <= currentTime)
        pair.Value?.Invoke();
}

ConcurrentDictionary、ConcurrentQueue 等

  • 特点:线程安全,内部带锁。
  • 使用场景:Unity 主线程逻辑中一般用不到,适合多线程工具层、后台线程与主线程之间共享集合时使用。

38.2 知识点代码

Lesson38_性能优化_CPU_脚本_数据结构选择.cs

using UnityEngine;

public class Lesson38_性能优化_CPU_脚本_数据结构选择 : MonoBehaviour
{
    void Start()
    {
        #region 知识点一 数据结构选择

        /* 在 Unity 中用合适的数据结构装载数据。
         * List:查找 O(n),连续存储,适合常遍历、中间插入少。
         * Dictionary:查找 O(1),不连续存储,适合常按键查找。*/

        #endregion

        #region 知识点二 更多数据结构选择

        /* HashSet<T>:查找/添加/删除 O(1),只关心存在性、无顺序时用。
         * Queue<T>:入队出队 O(1),先进先出,如对象池。
         * Stack<T>:入栈出栈 O(1),先进后出,如状态回退。
         * LinkedList<T>:中间插删 O(1)、查找 O(n),不连续内存,少用于频繁遍历。
         * SortedDictionary/SortedList:按 Key 有序,操作 O(log n),适合按序遍历键值对。
         * Concurrent*:线程安全,主线程逻辑一般不用,多线程工具层可用。*/

        #endregion
    }
}


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 785293209@qq.com

×

喜欢就点赞,疼爱就打赏