Unity3D中经常使用的数据结构总结与分析

来到周末,小匹夫终于有精力和时间来更新下博客了。前段时间小匹夫读过一份代码,对其中各类数据结构灵活的使用赞不绝口,同时也大大激发了小匹夫对各类数据结构进行梳理和总结的欲望。正好最近也拜读了若干大神的文章,以为总结下经常使用的数据结构以供本身也能灵活的使用变得刻不容缓。那么仍是从小匹夫的工做内容入手,就谈谈在平时使用U3D时经经常使用到的数据结构和各类数据结构的应用场景吧。html

1.几种常见的数据结构

 这里主要总结下小匹夫在工做中常碰到的几种数据结构:ArrayArrayListList<T>LinkedList<T>Queue<T>Stack<T>Dictionary<K,T>数组

 数组Array:

  数组是最简单的数据结构。其具备以下特色:安全

  1. 数组存储在连续的内存上。
  2. 数组的内容都是相同类型。
  3. 数组能够直接经过下标访问。

  数组Array的建立:数据结构

1 int size = 5;
2 int[] test = new int[size];

  建立一个新的数组时将在 CLR 托管堆中分配一块连续的内存空间,来盛放数量为size,类型为所声明类型的数组元素。若是类型为值类型,则将会有size个未装箱的该类型的值被建立。若是类型引用类型,则将会有size个相应类型的引用被建立。dom

  因为是在连续内存上存储的,因此它的索引速度很是快,访问一个元素的时间是恒定的也就是说与数组的元素数量无关,并且赋值与修改元素也很简单。post

string[] test2 = new string[3];
//赋值
test2[0] = "chen";
test2[1] = "j";
test2[2] = "d";
//修改
test2[0] = "chenjd";

  可是有优势,那么就必定会伴随着缺点。因为是连续存储,因此在两个元素之间插入新的元素就变得不方便。并且就像上面的代码所显示的那样,声明一个新的数组时,必须指定其长度,这就会存在一个潜在的问题,那就是当咱们声明的长度过长时,显然会浪费内存,当咱们声明长度太短的时候,则面临这溢出的风险。这就使得写代码像是投机,小匹夫很厌恶这样的行为!针对这种缺点,下面隆重推出ArrayList。url

 ArrayList:

  为了解决数组建立时必须指定长度以及只能存放相同类型的缺点而推出的数据结构。ArrayList是System.Collections命名空间下的一部分,因此若要使用则必须引入System.Collections。正如上文所说,ArrayList解决了数组的一些缺点。spa

  1. 没必要在声明ArrayList时指定它的长度,这是因为ArrayList对象的长度是按照其中存储的数据来动态增加与缩减的。
  2. ArrayList能够存储不一样类型的元素。这是因为ArrayList会把它的元素都当作Object来处理。于是,加入不一样类型的元素是容许的。

  ArrayList的操做:3d

ArrayList test3 = new ArrayList();
//新增数据
test3.Add("chen");
test3.Add("j");
test3.Add("d");
test3.Add("is");
test3.Add(25);
//修改数据
test3[4] = 26;
//删除数据
test3.RemoveAt(4);

  说了那么一堆”优势“,也该说说缺点了吧。为何要给”优势”打上引号呢?那是由于ArrayList能够存储不一样类型数据的缘由是因为把全部的类型都当作Object来作处理,也就是说ArrayList的元素其实都是Object类型的,辣么问题就来了。指针

  1. ArrayList不是类型安全的。由于把不一样的类型都当作Object来作处理,颇有可能会在使用ArrayList时发生类型不匹配的状况。
  2. 如上文所诉,数组存储值类型时并未发生装箱,可是ArrayList因为把全部类型都当作了Object,因此不可避免的当插入值类型会发生装箱操做,在索引取值时会发生拆箱操做。这能忍吗?

注:为什么说频繁的没有必要的装箱和拆箱不能忍呢?且听小匹夫慢慢道来:所谓装箱 (boxing):就是值类型实例到对象的转换(百度百科)。那么拆箱:就是将引用类型转换为值类型咯(仍是来自百度百科)。下面举个栗子~

//装箱,将String类型的值FanyoyChenjd赋值给对象。
int  info = 1989;  
object obj=(object)info;  

//拆箱,从Obj中提取值给info
object obj = 1;
int info = (int)obj;

 

那么结论呢?好吧,请容许小匹夫很low再次引用百度百科。显然,从原理上能够看出,装箱时,生成的是全新的引用对象,这会有时间损耗,也就是形成效率下降。

 

 List<T>泛型List

  为了解决ArrayList不安全类型与装箱拆箱的缺点,因此出现了泛型的概念,做为一种新的数组类型引入。也是工做中常常用到的数组类型。和ArrayList很类似,长度均可以灵活的改变,最大的不一样在于在声明List集合时,咱们同时须要为其声明List集合内数据的对象类型,这点又和Array很类似,其实List<T>内部使用了Array来实现。

List<string> test4 = new List<string>();  
  
//新增数据  
test4.Add(“Fanyoy”);  
test4.Add(“Chenjd”);  

//修改数据  
test4[1] = “murongxiaopifu”;  
  
//移除数据  
test4.RemoveAt(0);  

  这么作最大的好处就是

  1. 即确保了类型安全
  2. 取消了装箱和拆箱的操做。
  3. 它融合了Array能够快速访问的优势以及ArrayList长度能够灵活变化的优势。

  LinkedList<T>

  也就是链表了。和上述的数组最大的不一样之处就是在于链表在内存存储的排序上多是不连续的。这是因为链表是经过上一个元素指向下一个元素来排列的,因此可能不能经过下标来访问。如图

  既然链表最大的特色就是存储在内存的空间不必定连续,那么链表相对于数组最大优点和劣势就显而易见了。

  1. 向链表中插入或删除节点无需调整结构的容量。由于自己不是连续存储而是靠各对象的指针所决定,因此添加元素和删除元素都要比数组要有优点。
  2. 链表适合在须要有序的排序的情境下增长新的元素,这里还拿数组作对比,例如要在数组中间某个位置增长新的元素,则可能须要移动移动不少元素,而对于链表而言可能只是若干元素的指向发生变化而已。
  3. 有优势就有缺点,因为其在内存空间中不必定是连续排列,因此访问时候没法利用下标,而是必须从头结点开始,逐次遍历下一个节点直到寻找到目标。因此当须要快速访问对象时,数组无疑更有优点。

  综上,链表适合元素数量不固定,须要常常增减节点的状况。

  关于链表的使用,MSDN上有详细的例子

  Queue<T>

  在Queue<T>这种数据结构中,最早插入在元素将是最早被删除;反之最后插入的元素将最后被删除,所以队列又称为“先进先出”(FIFO—first in first out)的线性表。经过使用Enqueue和Dequeue这两个方法来实现对 Queue<T> 的存取。

  一些须要注意的地方:

  1. 先进先出的情景。
  2. 默认状况下,Queue<T>的初始容量为32, 增加因子为2.0。
  3. 当使用Enqueue时,会判断队列的长度是否足够,若不足,则依据增加因子来增长容量,例如当为初始的2.0时,则队列容量增加2倍。
  4. 乏善可陈。

  关于Queue<T>的使用方法,MSDN上也有相应的例子

  Stack<T>

  

  与Queue<T>相对,当须要使用后进先出顺序(LIFO)的数据结构时,咱们就须要用到Stack<T>了。

  一些须要注意的地方:

  1. 后进先出的情景。
  2. 默认容量为10。
  3. 使用pop和push来操做。
  4. 乏善可陈。

  一样,在这里你也能够看到大量Stack<T>的例子。

  Dictionary<K,T>

  字典这东西,小匹夫但是喜欢的不得了。看官们本身也能够想一想字典是否是很招人喜欢,建立一个字典以后就能够往里面扔东西,增长、删除、访问那叫一个快字了得。可是直到小匹夫日前看了一个大神的文章,才又想起了那句话“啥好事咋能让你都占了呢”。那么字典背后到底隐藏着什么迷雾,拨开重重迷雾以后,是否才是真相?且听下回分。。。等等,应该是下面就让咱们来分析一下字典吧。

  提到字典就不得不说Hashtable哈希表以及Hashing(哈希,也有叫散列的),由于字典的实现方式就是哈希表的实现方式,只不过字典是类型安全的,也就是说当建立字典时,必须声明key和item的类型,这是第一条字典与哈希表的区别。关于哈希表的内容推荐看下这篇博客哈希表。关于哈希,简单的说就是一种将任意长度的消息压缩到某一固定长度,好比某学校的学生学号范围从00000~99999,总共5位数字,若每一个数字都对应一个索引的话,那么就是100000个索引,可是若是咱们使用后3位做为索引,那么索引的范围就变成了000~999了,固然会冲突的状况,这种状况就是哈希冲突(Hash Collisions)了。扯远了,关于具体的实现原理仍是去看小匹夫推荐的那篇博客吧,固然那篇博客上面那个大大的转字也是蛮刺眼的。。。

  回到Dictionary<K,T>,咱们在对字典的操做中各类时间上的优点都享受到了,那么它的劣势到底在哪呢?对嘞,就是空间。以空间换时间,经过更多的内存开销来知足咱们对速度的追求。在建立字典时,咱们能够传入一个容量值,但实际使用的容量并不是该值。而是使用“不小于该值的最小质数来做为它使用的实际容量,最小是3。”(老赵),当有了实际容量以后,并不是直接实现索引,而是经过建立额外的2个数组来实现间接的索引,即int[] buckets和Entry[] entries两个数组(即buckets中保存的实际上是entries数组的下标),这里就是第二条字典与哈希表的区别,还记得哈希冲突吗?对,第二个区别就是处理哈希冲突的策略是不一样的!字典会采用额外的数据结构来处理哈希冲突,这就是刚才提到的数组之一buckets桶了,buckets的长度就是字典的真实长度,由于buckets就是字典每一个位置的映射,而后buckets中的每一个元素都是一个链表,用来存储相同哈希的元素,而后再分配存储空间。

所以,咱们面临的状况就是,即使咱们新建了一个空的字典,那么伴随而来的是2个长度为3的数组。因此当处理的数据很少时,仍是慎重使用字典为好,不少状况下使用数组也是能够接受的。

 

2.几种常见数据结构的使用情景

Array

须要处理的元素数量肯定而且须要使用下标时能够考虑,不过建议使用List<T>

ArrayList

不推荐使用,建议用List<T>

List<T>泛型List

须要处理的元素数量不肯定时 一般建议使用

LinkedList<T>

链表适合元素数量不固定,须要常常增减节点的状况,2端均可以增减

Queue<T>

先进先出的状况

Stack<T>

后进先出的状况

Dictionary<K,T>

须要键值对,快速操做

 

  

 

  

  

 

 

 

装模做样的声明一下:本博文章若非特殊注明皆为原创,若需转载请保留原文连接及做者信息慕容小匹夫

相关文章
相关标签/搜索