优化集合在开发中的使用

本文探讨了在游戏开发中Array、List和Dictionary等数据结构的应用及优化技巧。通过对比测试,展示了不同场景下各数据结构的性能表现,帮助开发者合理选择合适的数据结构。

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

这里有修改,读者可参考原文。 转自:http://mp.weixin.qq.com/s/Y0-0hS6qQ7-Bsgy_JmRrfw这

主要目的:通过详细了解Array、List和Dictionary这些数据结构,让你的游戏可以以10倍的速度来更快的运行。

什么是集合?

集合是用于数据存储和检索的专用类。集合类通常涵盖了将内存动态分配给元素以及基于索引等访问列表元素的目的。

这些类创建了Object类的对象集合,这是C#中所有数据类型的基类。与数组不同,集合可以根据应用程序的需要动态增长和缩减,这是它相对于数组的主要优势。

集合使得内存管理和数据管理的过程变得非常容易。

在Unity之中有哪些常见的集合?

1.列表

- C#中的List <T>类表示可以通过索引访问的对象的强类型列表,并且它支持存储特定类型的值,而不必向对象进行转换。
- 像任何其他集合一样,它允许数据处理功能,比如说是添加、插入、删除、搜索等等。
- 列表的索引就像数组一样,它的主要优势是其大小的动态性。
- 举个简单的例子来说,我们可以使GameObjects的列表为:

List<GameObject> myList=new List<GameObject>();


2.字典

- 字典实际上是一个更新后的哈希表类型的集合。
- 字典表示键和值的集合。
- 举个简单的例子来说,如果5代表红色,10代表绿色,我们可以通过说明5作为值的关键字在字典中将它与红色进行链接。
- 因此,我们可以总是通过记住键为5来找到红色。
- 这就是字典如何使得搜索数据更加容易。
- 举个简单的例子来说, 我们可以创建这样一个字典对象:
Dictionary<int,string> myDictionary=new Dictionary<int,string>();

现在这个博客的主要目标是优化集合的使用,我们将在这里跳过集合的学习部分。

集合是如何影响游戏的?

让我们举个例子,来了解集合对我们的游戏的影响。

我们在Unity测试一组代码,用Stopwatch来测一下时间:

(关于程序运行时间的测量,不清楚的可以看一下我的另一篇文章:http://blog.youkuaiyun.com/qq_33337811/article/details/60471116)

    //首先建立数组列表字典并初始化
    public int numberOfIterations = 10000000;
    private Stopwatch sw;
    private List<int> intList;
    private Dictionary<int, int> intDictionary;
    private int[] intArray;
    void Start()
    {
        sw = new Stopwatch();
        intList = new List<int>();
        intDictionary = new Dictionary<int, int>();
        intArray = new int[numberOfIterations];
        AddFakeValuesInArray(numberOfIterations);
        AddFakeValuesInDictionary(numberOfIterations);
        AddFakeValuesInList(numberOfIterations);
    }
    private void AddFakeValuesInArray(int num)
    {
        for (int i=0;i< num;i++)
        {
            intArray[i] = Random.Range(0,100);
        }
    }

    private void AddFakeValuesInList(int num)
    {
        for (int i = 0; i < num; i++)
        {
            intList.Add(Random.Range(0,100));
        }
        intList[num - 1] = 111;
    }

    private void AddFakeValuesInDictionary(int num)
    {
        for (int i = 0; i < num; i++)
        {
            intDictionary.Add(i,Random.Range(0,100));
        }
        intDictionary[num - 1] = 111;
    }

需要注意的是:

为了获得其性能的确切概念,我们对每个数据集合使用了1000万次的总迭代。

然后我们测试一下方法时间:

void Update()
    {
        if (Input.GetKeyDown(KeyCode.Space))
        {
            PerformTest();
        }
        if (Input.GetKeyDown(KeyCode.S))
        {
            SearchInList(111);
            SearchInDictionary(numberOfIterations-1);
            UnityEngine.Debug.Log("SearchComplete!");
        }
    }
     private void SearchInList(int value)
    {
        //列表中寻找
        sw.Start();
        int index = intList.FindIndex(item=>item==value);
        sw.Stop();
        UnityEngine.Debug.Log("Index:"+index);//9999999
        UnityEngine.Debug.Log("Time taken to find in list:"+sw.ElapsedMilliseconds+"ms.");//138
        sw.Reset();
        //检查列表是否包含值
        sw.Start();
        bool containValue = intList.Contains(value);
        sw.Stop();
        UnityEngine.Debug.Log(containValue);//True
        UnityEngine.Debug.Log("Time taken to check in list:"+sw.ElapsedMilliseconds+"ms.");//223
        sw.Reset();
    }

    private void SearchInDictionary(int key)
    {
        //字典按键找值
        sw.Start();
        int value = intDictionary[key];
        sw.Stop();
        UnityEngine.Debug.Log("Time taken to find in dictionary:"+sw.ElapsedMilliseconds+"ms.");//0
        sw.Reset();
        //检查是否字典包含键
        sw.Start();
        bool containKey = intDictionary.ContainsKey(key);
        sw.Stop();
        UnityEngine.Debug.Log(containKey);//true
        UnityEngine.Debug.Log("Time taken to check if it contains key in Dictionary:" + sw.ElapsedMilliseconds + "ms.");//0
        sw.Reset();
        //检查是否字典包含值
        sw.Start();
        bool containValue = intDictionary.ContainsValue(key);
        sw.Stop();
        UnityEngine.Debug.Log(containValue);//false
        UnityEngine.Debug.Log("Time taken to check if it contains value in Dictionary:" + sw.ElapsedMilliseconds + "ms.");//417
        sw.Reset();
    }

private void PerformTest()
    {
        //数组循环
        sw.Start();
        for (int i=0;i<intArray.Length;i++)
        {

        }
        sw.Stop();
        UnityEngine.Debug.Log("Time taken by array "+sw.ElapsedMilliseconds+"ms.");//36ms
        sw.Reset();
        //列表循环
        sw.Start();
        for (int i = 0; i < intList.Count; i++)
        {

        }
        sw.Stop();
        UnityEngine.Debug.Log("Time taken by list " + sw.ElapsedMilliseconds + "ms.");//98ms
        sw.Reset();
        //列表foreach循环
        sw.Start();
        foreach (var item in intList)
        {

        }
        sw.Stop();
        UnityEngine.Debug.Log("Time taken by list using foreach"+sw.ElapsedMilliseconds+"ms.");//371ms
        sw.Reset();
        //字典循环遍历
        sw.Start();
        foreach (var key in intDictionary.Keys)
        {

        }
        sw.Stop();
        UnityEngine.Debug.Log("Time taken by dictionary" + sw.ElapsedMilliseconds + "ms.");//894ms
        sw.Reset();
    }


注意PerformTest()的打印结果:

- 这是什么意思?数组是最好的选择么? 我们应该停止使用其他的集合吗?
  根本不是这样,正如我在开始时提到的那样,所有的集合设计的时候都有自己针对的用途。
- 只是我们必须明智地知道哪个集合才是真正符合我们需求的集合。
- 让我们来了解下在哪些情况下应该使用什么样的数据结构:

情况1:在整个游戏过程中对象的数量保持不变。
- 现在这里不值得使用列表或是字典,因为显然对象的数量不会改变。那么为什么还要使用集合为内存和处理器增加额外的负担?
- 这里数组的性能是列表的性能的两倍!


情况2:在游戏过程中对象的数量不断变化。
- 因为我们知道数组不是动态的,所以一个很明显的选择是列表。由于对象的数量不断变化,与字典相比管理速度更快。
- 在对象池的情况下,列表通常用于管理对象池。
- 列表比字典快了8-10倍。
- 如果使用foreach循环遍历列表,它几乎需要比正常for循环多3倍的时间,如上面的例子所示,这因此也是一个使用foreach循环的缺点。

我应该完全停止使用字典?

不,不是。我们看下 SearchInList(111); SearchInDictionary(numberOfIterations-1);这两个方法。

很明显的结论是,当谈到字典的时候,搜索时间几乎是零。

你不能创造一个没有集合的游戏

是的,这是真的,这也不坏。只有恰当地了解其内部机制才能实现高质量的数据结构管理。

----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

结论很简单,有三个基本准则:

1) 当多个对象保持相同且不需要频繁的搜索的时候,不要使用列表。

2) 如果对象是动态的并且搜索的级别不是很优先,那么列表是很好的方式!

3)如果需要快速访问以及对象较少发生变化,那么词典将是明智的选择。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值