C#基礎語法中得數組,定義就是:數組是一種數據結構,包含同一個類型的多個元素。從CLR的角度來看,首先數組是引用類型,堆棧分配屬於分配於堆上,其次數組在內存中是連續的存儲的,因此索引速度很快,並且賦值與修改元素也很簡單。能夠利用偏移地址訪問元素,時間複雜度爲O(1);能夠用折半查找法查找元素,效率高。數組
上面這段話初看之下很莫名奇妙,解釋一下,首先基礎語法定義了數組是什麼,是一種數據結構,數據結構有不少種:棧,隊列,列表,字典,樹,圖,數組也是這樣一種數據結構,並且是不少其餘數據結構的基本,可是數組的內的數據類型只能是一種,在一個數組中不能既存在int又存在string安全
數組,ArrayList,List區別數據結構
在從CLR的角度來看,CLR介紹數組時,就從其派生自System.Array,而System.Array派生自Object,因此數組是引用類型,分配於託管堆上,而且數組在託管堆上分配到的是一塊連續存儲的內存,能夠經過索引查找,雖然查詢的時間複雜度是O(1),但有個問題被忽略了,就是找的快未必就有用,數組分配在一塊連續的數據空間上,所以分配空間時必須肯定大小。空間的連續,也致使了存儲效率低,插入和刪除元素效率比較低,並且麻煩。若是,要增添一個元素,須要移動大量元素,在內存中空出一個元素的空間,而後將要增長的元素放在其中。一樣,你想刪除一個元素,須要移動大量元素去填補被移動的元素。這就是數組的詬病,爲了解決這個問題C#又推出了ArrayListide
ArrayList是.Net Framework提供的用於數據存儲和檢索的專用類,它是命名空間System.Collections下的一部分。它的大小是按照其中存儲的數據來動態擴充與收縮的。因此,咱們在聲明ArrayList對象時並不須要指定它的長度。但微軟又發現使用ArrayList存在一個更爲致命的問題,看下面的代碼函數
1 //初始化ArrayList 2 ArrayList list = new ArrayList(); 3 //新增數據 4 list.Add("abc"); 5 list.Add(123); 6 //插入數據 7 list.Insert(0, "在第一個位置插入一條數據"); 8 foreach (var item in list) 9 { 10 Console.WriteLine(item); 11 }
在list中,咱們不只插入了字符串"abc",並且又插入了數字123。這樣在ArrayList中插入不一樣類型的數據是容許的。由於ArrayList會把全部插入其中的數據都看成爲object類型來處理。這樣,在咱們使用ArrayList中的數據來處理問題的時候,極可能會報類型不匹配的錯誤,也就是說ArrayList不是類型安全的。既使咱們保證在插入數據的時候都很當心,都有插入了同一類型的數據,但在使用的時候,咱們也須要將它們轉化爲對應的原類型來處理。這就存在了裝箱與拆箱的操做,會帶來很大的性能損耗。性能
雖然ArrayList解決了數組長度問題,可是留下了類型安全問題,裝箱和拆箱操做一直是操做程序中所努力在避免的操做,由於其對性能的損耗確實是很大的,因此C#推出了Lits集合,下面的代碼是聲明實現一個List的集合,能夠看到List指定了類型,避免了拆箱裝箱的操做,而且也無需指定元素個數。spa
1 List<int> list = new List<int>(); 2 //新增數據 3 list.Add(123); 4 //報錯,沒法從string轉化成int 5 list.Add("123"); 6 foreach (var item in list) 7 { 8 Console.WriteLine(item); 9 }
事實上,List來自對ArrayList的封裝,而ArrayList封裝自數組,數組是ArrayList和List的底層實現,那麼是否是使用List就能夠完美的代替數組了呢?之後全部的代碼都只須要寫成List就行了,既然List是來自數組的封裝,繼承了數組的全部方法和行爲,其實這裏有一次回到了效率問題,若是是固定的而且知道其長度的數據集合用數組,而不固定長度的用List。額講了這麼多,或許就是這句話最重要吧。指針
建立下限非零的數組code
C#中有兩種數組,一種是下表從0開始的數組,一種是下標非0開始的,不推薦使用下標非0開始的,經過下面的代碼就能夠建立一個指定下限的多維數組對象
1 static void Main(string[] args) 2 { 3 // 建立和初始化多維數組字符串類型。 4 int[] myLengthsArray = new int[2] { 3, 5 }; 5 int[] myBoundsArray = new int[2] { 2, 3 }; 6 //CreateInstance 建立指定下標的數組的方法 7 //elementTypeType: System.Type要建立的 Array 的 Type。 8 //一維數組,它包含要建立的 Array 的每一個維度的大小。 9 //一維數組,它包含要建立的 Array 的每一個維度的下限(起始索引)。 10 Array myArray = Array.CreateInstance(typeof(String), myLengthsArray, myBoundsArray); 11 12 for (int i = myArray.GetLowerBound(0); i <= myArray.GetUpperBound(0); i++) 13 for (int j = myArray.GetLowerBound(1); j <= myArray.GetUpperBound(1); j++) 14 { 15 int[] myIndicesArray = new int[2] { i, j }; 16 myArray.SetValue(Convert.ToString(i) + j, myIndicesArray); 17 } 18 19 //顯示每個維度的上限和下限 20 Console.WriteLine("Bounds:\tLower\tUpper"); 21 for (int i = 0; i < myArray.Rank; i++) 22 Console.WriteLine("{0}:\t{1}\t{2}", i, myArray.GetLowerBound(i), myArray.GetUpperBound(i)); 23 // 顯示的值的數組。 24 Console.WriteLine("The Array contains the following values:"); 25 PrintValues(myArray); 26 } 27 public static void PrintValues(Array myArr) 28 { 29 IEnumerator myEnumerator = myArr.GetEnumerator(); 30 int i = 0; 31 int cols = myArr.GetLength(myArr.Rank - 1); 32 while (myEnumerator.MoveNext()) 33 { 34 if (i < cols) 35 { 36 i++; 37 } 38 else 39 { 40 Console.WriteLine(); 41 i = 1; 42 } 43 Console.Write("\t{0}", myEnumerator.Current); 44 } 45 Console.WriteLine(); 46 }
數組的傳遞和返回
其實傳遞就是看成參數傳遞給函數,返回就是能夠做爲類型返回接收,但有個問題,數組是引用類型,開篇咱們就介紹了,傳遞給函數,若是函數改動了數組,原數組就會跟着一塊兒變,這確定不是咱們想要的。解決方案是能夠直接複製一個數組,經過Array.Copy()方法,複製一個新的數組傳遞給函數體,若是不但願原數組被改動的話。
不安全的數組訪問
咱們每次訪問一個數組中的元素時,CLR都會確保索引不會超出數組的上下限。CLR的索引檢查會有一些性能方面的代價。若是你們對本身的代碼有足夠的信心,而且不介意使用非安全代碼,則可在訪問一個數組時不讓CLR執行索引檢查。
1 //unsafe 開啓不安全訪問標誌 2 //須要在你的項目屬性頁面裏面,把是否包含unsafe代碼的選項選上 3 //不然會報錯 4 unsafe static void main() 5 { 6 Int32[] arr = new Int32{1, 2, 3, 4, 5}; 7 //獲取一個指向數組第0元素的指針 8 fixed(Int32 * element = &arr[0]) 9 { 10 for(Int32 x = 0, n = arr.Length; x < n; x++) 11 { 12 Console.WriteLine(element[x]); 13 } 14 } 15 }
小結
今天的內容理論偏多代碼偏少,由於關於數組我以爲以CLR的角度看,不須要寫聲明,增刪查,二維多維數組這些概念,還有數組的排序和複製,若是把數組的一個一個方法拿出來介紹可能並無多少必要性,而是分析一下數組在底層是一個怎樣的分配,它和ArrayList還有List是什麼關係,其實數組能夠說是不少數據結構的基礎。