Unity的优化系列3 - 理解数组、 列表和字典的详细
孙广东 2016.5.24
集合的影响有哪些?
难道应该完全停止使用字典集合么?
总之,几毫秒的缓慢循环都会让玩家发疯!!!(集合都太慢了) 其实在游戏开发中经常会遇到对集合数据的排序:
在应用程序中我们管理相关对象通过以下两种方式之一:
- 通过创建对象数组(创建就不变大小)
- 通过创建对象的集合(可以动态改变大小)
每一种集合都有特定的用途,都有自己的优点和缺点。
List<GameObject> myListOfGameObjects = new List<GameObject>(); Dictionary<int, String> myDictionary = new Dictionary<int, String>();
新建一个脚本 作为测试:
public class GenericCollectionsTest : MonoBehaviour { #region PUBLIC_DECLARATIONS public int numberOfIterations = 10000000; #endregion #region PRIVATE_DECLARATIONS private Stopwatch stopWatch; private List<int> intList; // 整数列表 private Dictionary<int,int> intDictionary; // 一本字典,键和值为整数。 private int[] intArray; // 一个整数数组 #endregion #region UNITY_CALLBACKS void Start() { stopWatch = new Stopwatch(); intArray = new int[numberOfIterations]; intList = new List<int>(); intDictionary = new Dictionary<int, int>(); AddFakeValuesInArray(numberOfIterations); AddFakeValuesInList(numberOfIterations); AddFakeValuesInDictionay(numberOfIterations); } void Update() { if (Input.GetKeyDown(KeyCode.Space)) { PerformTest(); } if (Input.GetKeyDown(KeyCode.S)) { SearchInList(111); SearchInDictionary(numberOfIterations - 1); UnityEngine.Debug.Log("SearchComplete"); } } #endregion #region PRIVATE_METHODS private void AddFakeValuesInArray(int iterations) { for (int i = 0; i < iterations; i++) { intArray[i] = Random.Range(0, 100); } } private void AddFakeValuesInList(int iterations) { for (int i = 0; i < iterations; i++) { intList.Add(Random.Range(0, 100)); } intList[iterations - 1] = 111; } private void AddFakeValuesInDictionay(int iterations) { for (int i = 0; i < iterations; i++) { intDictionary.Add(i, Random.Range(0, 100)); } intDictionary[iterations - 1] = 111; } private void SearchInList(int value) { #region FIND_IN_LIST stopWatch.Start(); int index = intList.FindIndex(item => item == value); stopWatch.Stop(); UnityEngine.Debug.Log("Index " + index); UnityEngine.Debug.Log(“Time Taken to Find in List ”+stopWatch.ElapsedMilliseconds+” ms”); stopWatch.Reset(); #endregion #region CHECK_IF_CONTAINS_VALUE_IN_LIST stopWatch.Start(); bool containsValue = intList.Contains(value); stopWatch.Stop(); UnityEngine.Debug.Log(containsValue); UnityEngine.Debug.Log(“Time Taken To Check in List ”+stopWatch.ElapsedMilliseconds+” ms”); stopWatch.Reset(); #endregion } private void SearchInDictionary(int key) { #region FIND_IN_DICTIONARY_USING_REQUIRED_KEY stopWatch.Start(); int value = intDictionary[key]; stopWatch.Stop(); UnityEngine.Debug.Log((“Time Taken to Find in Dictionary ”+stopWatch.ElapsedMilliseconds+” ms”); stopWatch.Reset(); #endregion #region CHECK_IF_DICTIONARY_CONTAINS_VALUE stopWatch.Start(); bool containsKey = intDictionary.ContainsKey(key); stopWatch.Stop(); UnityEngine.Debug.Log(containsKey); UnityEngine.Debug.Log("Time taken to check if it contains key in Dictionary" + stopWatch.ElapsedMilliseconds+ “ ms”); stopWatch.Reset(); #endregion } private void PerformTest() { #region ARRAY_ITERATION // 循环遍历数组 stopWatch.Start(); for (int i = 0; i < intArray.Length; i++) { } stopWatch.Stop(); UnityEngine.Debug.Log(“Time Taken By Array ”+stopWatch.ElapsedMilliseconds+ ”ms”); stopWatch.Reset(); #endregion #region LIST_ITERATION // 循环遍历列表中使用简单的 for 循环 stopWatch.Start(); for (int i = 0; i < intList.Count; i++) { } stopWatch.Stop(); UnityEngine.Debug.Log(“Time Taken By List ”+stopWatch.ElapsedMilliseconds+ ”ms”); stopWatch.Reset(); #endregion #region LIST_ITERATION_BY_FOREACH_LOOP // 遍历列表中通过使用 foreach 循环 stopWatch.Start(); foreach (var item in intList) { } stopWatch.Stop(); UnityEngine.Debug.Log(“Time Taken By List Using foreach ”+stopWatch.ElapsedMilliseconds+ ”ms”); stopWatch.Reset(); #endregion #region DICTIONARY_ITERATIOn_LOOP // 遍历字典 stopWatch.Start(); foreach (var key in intDictionary.Keys) { } stopWatch.Stop(); UnityEngine.Debug.Log(“Time Taken By Dictionary ”+stopWatch.ElapsedMilliseconds+ ”ms”); stopWatch.Reset(); #endregion } #endregion }
关于秒表类Stopwatch ,用于测试函数的执行时间特别方便。 请参阅以下链接 ︰ http://www.dotnetperls.com/stopwatch (注意这个类是官方的API)
运行之后的输出如下:
让我们来了解数据结构,我们应该使用 考虑的几例 ︰
案例 1) 对象的数量保持不变在整个游戏
显然不会改变对象的数目, 现在在这里就不值得用作列表或字典。
在这里数组的性能列表快2倍 !
案例 2) 对象的数量在游戏过程中不断变化
显而易见的选择是列表。作为对象不停地变化,它是比字典更快. 如果对象池,列出了常用来管理池。
列表与词典进行比较几乎是8-10 倍快,如果您遍历使用foreach循环List 几乎才会 3 倍的时间比正常的循环,从而增加了使用foreach循环的一个更多缺点上面的示例所示。如果你想要看看其他缺点的foreach循环 。
难道我应该完全停止使用的词典吗?
字典的查找 索引可是强项:
SearchInList()方法的第一部分是来查找列表中传递给它的值,检查如果它实际上包含的值,并返回布尔值
SeatchInDictionary()方法的第一部分是要根据传递给它,键的值和第二部分将检查如果该方法具有特定键或不存在,通过使用ContainsKey() 再次按键调用这些方法的。
在看Log的输出:
所以从图片显而易见的结论是,字典搜索时间是几乎为零。 所以每当要不断寻找一些对象在整个游戏的情况下,明智的选择就是选择词典!
结论是简单的有三个基本准则 ︰
- 当对象数目仍然相同,也有是没有要求的密集搜索不使用列表。
- 如果对象是动态和搜索不是一个优先事项,列表!
- 快速访问和对象,那么词典将是明智的选择
你也可以通过以下链接以获取更多的深入研究,相同的 ︰ http://www.dotnetperls.com/dictionary-time http://answers.unity3d.com/questions/38479/is-listt-as-fast-to-access-as-a-standard-array.html
- 上一篇: OJ平台中的一个数组初始化问题
- 下一篇: [循环打印]数组套数组