來到週末,小匹夫終於有精力和時間來更新下博客了。前段時間小匹夫讀過一份代碼,對其中各類數據結構靈活的使用讚不絕口,同時也大大激發了小匹夫對各類數據結構進行梳理和總結的慾望。正好最近也拜讀了若干大神的文章,以爲總結下經常使用的數據結構以供本身也能靈活的使用變得刻不容緩。那麼仍是從小匹夫的工做內容入手,就談談在平時使用U3D時經經常使用到的數據結構和各類數據結構的應用場景吧。html
這裏主要總結下小匹夫在工做中常碰到的幾種數據結構:Array,ArrayList,List<T>,LinkedList<T>,Queue<T>,Stack<T>,Dictionary<K,T>數組
數組是最簡單的數據結構。其具備以下特色:安全
數組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是System.Collections命名空間下的一部分,因此若要使用則必須引入System.Collections。正如上文所說,ArrayList解決了數組的一些缺點。spa
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類型的,辣麼問題就來了。指針
注:爲什麼說頻繁的沒有必要的裝箱和拆箱不能忍呢?且聽小匹夫慢慢道來:所謂裝箱 (boxing):就是值類型實例到對象的轉換(百度百科)。那麼拆箱:就是將引用類型轉換爲值類型咯(仍是來自百度百科)。下面舉個栗子~
//裝箱,將String類型的值FanyoyChenjd賦值給對象。 int info = 1989; object obj=(object)info; //拆箱,從Obj中提取值給info object obj = 1;
int info = (int)obj;
那麼結論呢?好吧,請容許小匹夫很low再次引用百度百科。顯然,從原理上能夠看出,裝箱時,生成的是全新的引用對象,這會有時間損耗,也就是形成效率下降。
爲了解決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);
這麼作最大的好處就是
也就是鏈表了。和上述的數組最大的不一樣之處就是在於鏈表在內存存儲的排序上多是不連續的。這是因爲鏈表是經過上一個元素指向下一個元素來排列的,因此可能不能經過下標來訪問。如圖
既然鏈表最大的特色就是存儲在內存的空間不必定連續,那麼鏈表相對於數組最大優點和劣勢就顯而易見了。
綜上,鏈表適合元素數量不固定,須要常常增減節點的狀況。
關於鏈表的使用,MSDN上有詳細的例子。
在Queue<T>這種數據結構中,最早插入在元素將是最早被刪除;反之最後插入的元素將最後被刪除,所以隊列又稱爲「先進先出」(FIFO—first in first out)的線性表。經過使用Enqueue和Dequeue這兩個方法來實現對 Queue<T> 的存取。
一些須要注意的地方:
關於Queue<T>的使用方法,MSDN上也有相應的例子。
與Queue<T>相對,當須要使用後進先出順序(LIFO)的數據結構時,咱們就須要用到Stack<T>了。
一些須要注意的地方:
一樣,在這裏你也能夠看到大量Stack<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的數組。因此當處理的數據很少時,仍是慎重使用字典爲好,不少狀況下使用數組也是能夠接受的。
Array |
須要處理的元素數量肯定而且須要使用下標時能夠考慮,不過建議使用List<T> |
ArrayList |
不推薦使用,建議用List<T> |
List<T>泛型List |
須要處理的元素數量不肯定時 一般建議使用 |
LinkedList<T> |
鏈表適合元素數量不固定,須要常常增減節點的狀況,2端均可以增減 |
Queue<T> |
先進先出的狀況 |
Stack<T> |
後進先出的狀況 |
Dictionary<K,T> |
須要鍵值對,快速操做 |