【轉】編寫高質量代碼改善C#程序的157個建議——建議16:元素數量可變的情況下不應使用數組
建議16:元素數量可變的情況下不應使用數組
在C#中,數組一旦被創建,長度就不能改變。如果我們需要一個動態且可變長度的集合,就應該使用ArrayList或List<T>來創建。 而數組本身,尤其是一維數組,在遇到要求高效率的算法時,則會專門被優化以提升其效率。一維數組也成為向量,其性能是最佳的,在IL中使用了專門的指令來 處理它們(如newarr、ldelem、ldelema、ldelen和stelem)。
從內存的使用角度來講,數組在創建時被分配了一段固定長度的內存。如果數組的元素是值類型,則每個元素的長度等於相應的值類型的長度;如果數組的元素是引用類型,則每個元素的長度為該引用類型的IntPtr.Size(IntPtr:It‘s a class that wraps a pointer that is used when calling Windows API functions. The underlying pointer may be 32 bit or 64 bit, depending on the platform.)。數組的存儲結構一旦被分配,就不能再變化。而ArrayList是鏈表結構,可以動態的增減內存空間,如果ArrayList存儲的是值類型,則會為每個元素增加12字節的空間,其中4個字節擁有對象引用,8字節是元素裝箱時引入的對象頭。List<T>是ArrayList的泛型實現,它省去了裝箱和拆箱帶來的開銷。
註意:
由於數組本身在內存上的特點,因此在使用數組的過程中還應該註意大對象的問題。所謂“大對象”,是指那些占內存超過85000字節的對象,它們被分配在大對象堆裏。大對象的分配和回收和小對象都不太一樣,尤其是回收,大對象在回收過程中會帶來效率很低的問題。所以,不能對數組指定過大的長度,這會讓數組成為一個大對象。
如果一定要動態改變數組的長度,一種方法是將數組轉換為ArrayList或List<T>,如下面代碼說是:
int[] iArr = { 0, 1, 2, 3, 4, 5, 6 }; ArrayList arrayListInt= new ArrayList(iArr); //將數組轉變為ArrayList arrayListInt.Add(7); List<int> listInt = iArr.ToList<int>(); //將數組轉變為List<T> listInt.Add(7);
還有一種方法是數組的復制功能。數組繼承自System.Array,抽象類System.Array提供了一些有用的實現方法。其中就包括Copy方法,它負責將一個數組的內容復制到另外一個數組中。無論哪種方法,改變數組長度就相當於重新創建了一個數組對象。
為了讓數組看上去本身就具有動態改變長度的功能,可以創建一個名為Resize的擴展方法,代碼如下所示:
public static class ClassForExtensions { public static Array ReSize(this Array array, int newSize) { Type t = array.GetType().GetElementType(); Array newArray = Array.CreateInstance(t, newSize); Array.Copy(array, 0, newArray, 0, Math.Min(array.Length, newSize)); return newArray; } }
調用方法看起來如下:
int[] iArr = { 0, 1, 2, 3, 4, 5, 6 }; iArr = (int[])iArr.ReSize(10);
下面對改變數組長度和改變Lisit<T>長度的耗時做一個比較,以便強調本建議的主題:在元素數量可變的情況下不應該使用數組。
private static void ResizeArray() { int[] iArr = { 0, 1, 2, 3, 4, 5, 6 }; Stopwatch watch = new Stopwatch(); watch.Start(); iArr = (int[])iArr.ReSize(10); watch.Stop(); Console.WriteLine("ResizeArray: " + watch.Elapsed); } private static void ResizeList() { List<int> iArr = new List<int>(new int[] { 0, 1, 2, 3, 4, 5, 6 }); Stopwatch watch = new Stopwatch(); watch.Start(); iArr.Add(0); iArr.Add(0); iArr.Add(0); watch.Stop(); Console.WriteLine("ResizeList: " + watch.Elapsed); }
輸出為:
ResizeArray:00:00:00.0004441
ResizeList:00:00:0:0000036
當然,嚴格意義上講,List<T>不存在改變長度的說法,本建議只是為了比較,將iArr的長度變為10,同時還進行了賦值。即便這樣,我們可以看到,在時間效率上ResizeList比ResizeArray要高100倍以上。
轉自:《編寫高質量代碼改善C#程序的157個建議》陸敏技
【轉】編寫高質量代碼改善C#程序的157個建議——建議16:元素數量可變的情況下不應使用數組