- 新手學ASP.NET 3.5網絡開發
- 孔琳俊 陳松等編著
- 87字
- 2018-12-27 20:32:31
5 數組和集合
上一章介紹了.NET中的字符串操作技術,下面繼續介紹另一種常用的數據類型:數組和集合。同字符串一樣,數組和集合也是Web開發中最重要的類型之一,用于表示一組性質相近的對象。
5.1 數組
數組能夠按一定規律把相關的數據組織在一起,并通過“索引”或“下標”快速存取這些數據。本節首先介紹數組的基本概念,理解這些概念是學習使用數組的基礎。
5.1.1 什么是數組
【本節示例參考:\示例代碼\C05\Example_Array】
數組即一組數據,它把一系列數據組織在一起,成為一個可操作的整體。例如,當一個做事細心的妻子去超市買東西時,或許會事先列出一個清單:
(1)油;
(2)鹽;
(3)醬;
(4)醋;
(5)毛毛熊;
(6)……
可以稱這個清單為“需購物品”,它有規律地列出了其內部的數據,且其內部數據具有相同的性質。在程序語言中,可以稱這樣一個清單為數組:
//聲明數組 String[] myStrArr =new String[5]{ "油", "鹽", "醬", "醋", "毛毛熊" };
在數組中,其中的每一個元素對應排列次序下標。當使用其中的某個元素時,可以直接利用這個次序下標,具體如下:
1. //輸出數組元素 2. for(int i=0;i<5;i++) 3. { 4. Page.Response.Write("myStrArr["+i+"]="+myStrArr[i]+"<br/>"); 5. }
將輸出數組myStrArr中所有的元素,如圖5.1所示。

圖5.1 輸出數組元素
說明
同C語言和大部分語言一樣,C#的下標也是從0開始,而不是從1開始。
數組中的元素可以是任意的類型,如上面給出的示例,其中的每個元素都是字符串類型。除此之外,元素還可以是其他的基本數據類型,甚至又是一個數組。如果數組的元素又是數組,那么這個數組稱為多維數組,下面是一個二維數組的例子:
//聲明一個二維數組 String[,] myStrArr2 =new String[3,2]{ { "油", "鹽" }, { "《圍城》", "《晨露》" }, { "毛毛熊", "Snoopy" } };
在這個例子中,數組的第一維包含了三個元素,而每一個元素又是一個數組,分別包含了兩個元素。此時稱數組的秩為2;第一維的元素類型為數組,長度為3;第二維的元素類型為字符串,長度為2。
通過這個具體的例子,讀者可以思考“秩”、“維”、“元素類型”,以及“長度”的概念。同一維數組一樣,多維數組中的每一個元素,也可以通過下標方式來引用,如:
myStrArr[0]指一維數組{“油”,“鹽”}。
myStrArr[0,0]指字符串“油”。
下面的代碼,可以輸出myStrArr2中的所有元素。
代碼5-1 輸出數組元素:Default.aspx.cs
1. //輸出二維數組元素 2. for(int i=0;i<3;i++) 3. { 4. Page.Response.Write("--myStrArr2["+i+"]<br/>"); 5. for(int j=0;j<2;j++) 6. { 7. Page.Response.Write("----myStrArr2["+i+","+j+"]="+myStrArr2[i,j]+"<br/>"); 8. } 9. }
輸出結果如圖5.2所示。

圖5.2 分層輸出二維數組
5.1.2 創建數組
【本節示例參考:\示例代碼\C05\Array_Create】
在C#中,使用如下語法創建一個數組。
1.一維數組
data type[]arr name=new data type[int length]
這種方式定義一個元素數據類型為data type、長度為length的數組arr name,例如:
int[]myIntArr=new int[100]; //定義一個長度為100的int數組 string[]mystringArr=new string[100]; //定義一個長度為100的string數組 object[]myObjectArr=new object[100]; //定義一個長度為100的object數組
其中,數據類型data type既可以是常用數據類型(如int、float等),也可以是對象(如String、StringBuilder等)。
data type[]arr name=new data type[]{item1,item2,…,itemn}
這種方式定義一個元素數據類型為data type,并通過“=”運算符進行賦值,其初始值為所給出的元素{item1,item2,…,itemn}的個數,例如:
int[]myIntArr2=new int[]{1,2,3}; //定義一個int數組,長度為3 string[]mystringArr2=new string[]{"油","鹽"}; //定義一個string數組,長度為2
在這種定義下,不必給出數組的長度定義,數組的長度自動設置,為所給出的元素{item1,item2,…,itemn}的個數。即下面的兩種定義完全相同:
int[] myIntArr2=new int[]{1,2,3}; int[] myIntArr2=new int[3]{1,2,3};
2.多維數組
data type[,…,]arr name=new data type[int length1,int length2,…,int lengthn]
這種方式定義一個元素數據類型為data type,秩為n,各維長度分別為length1,length2,…,lengthn的數組arr name,例如:
int[,]myIntArr=new int[10,100]; //定義一個10*100的二維int數組 string[,,]mystringArr=new string[2,2,3]; //定義一個2*2*3的三維string數組
這里就定義了兩個多維數組。也可以用下面的方法進行多維數組的定義:
data_type[,…,] arr_name = new data_type[,…,]] { {item1, item2, … ,itemn} … }
例如:
int[,]myIntArr2=new int[,]{{1,2,3},{-1,-2,-3}}; //2*3的二維int數組 string[,]mystringArr2=new string[,]{{"油","鹽"},{"《圍城》","《晨露》"}}; //2*2的二維string數組
同一維數組一樣,在這種定義下,可以不必給出各維的長度定義,各維長度根據所給出的賦值元素自動確定。
3.交錯數組
C#支持各個維度長度不同的多維數組,稱為交錯數組,也稱為“數組的數組”。交錯數組的定義如下:
data type[][]…arr name=new data type[int length1][int length2]…
這個定義和定義多維數組非常類似,區別在于,交錯數組必須單獨初始化交錯數組每一維中的元素。例如,下面定義一個第一維長度為3的交錯數組:
1. int[][]myJaggedArray=new int[3][]; 2. myJaggedArray[0]=new int[5]; 3. myJaggedArray[1]=new int[4]; 4. myJaggedArray[2]=new int[2];
這個交錯數組myJaggedArray,每個元素都是一個一維整數數組。第一個元素是由5個整數組成的數組,第二個是由4個整數組成的數組,而第三個是由2個整數組成的數組。
data_type[][]…arr_name=new data_type[][]… { new data_type[]{item1,…,new data_type[]itemn} … }
這種方式在聲明數組的同時進行初始化,同樣可以不指定各維的長度,例如:
1. int[][]myJaggedArray=new int[][] 2. { 3. new int[]{1,3,5,7,9}, 4. new int[]{0,2,4,6}, 5. new int[]{11,22} 6. };
5.1.3 數組基類Array
System.Array類是所有.NET中數組的基類,提供創建、操作、搜索和排序數組的方法,其屬性和方法如圖5.3所示。

圖5.3 System.Array類
Array類常用屬性和方法的簡單說明,如表5.1所示。
表5.1 Array類常用屬性/方法說明

5.1.4 訪問數組元素
【本節示例參考:\示例代碼\C05\Array_AccessItem】
訪問數組的元素包括讀取或設置某個元素的值,最基本的方法是通過下標定位元素,另外還可以使用GetValue/SetValue方法。
1.通過下標定位元素
C#中數組對其中的元素進行排序,并從0開始計數,這樣每一個元素都會有一個唯一的下標,通過這個下標,就可以定位唯一的一個元素。下面通過示例來說明。
(1)一維數組:
string[]myStrArr={"油", "鹽", "醬", "醋", "毛毛熊"};
這里,myStrArr[0]=“油”;myStrArr[4]=“毛毛熊”。如果試圖訪問超過下標范圍的數據,則會出現如下異常:
System.IndexOutOfRangeException: 索引超出了數組界限
(2)多維數組:
string[,] myStrArr2={{"油","鹽"},{"《圍城》","《晨露》"},{"毛毛熊","Snoopy"}};
定義之后,myStrArr2[0,0]=“油”;myStrArr2[2,1]=“Snoopy”。
(3)交錯數組:
1. int[][]myJaggedArray=new int[][] 2. { 3. new int[]{1,3,5,7,9}, 4. new int[]{0,2,4,6}, 5. new int[]{11,22} 6. };
定義之后則有:myJaggedArray[0][0]=1;myJaggedArray[1][1]=2;myJaggedArray[2][1]=22。
下面的代碼可以循環輸出所有的交錯數組元素。
代碼5-2 輸出交錯數組元素:Default.aspx.cs
1. for(int i=myJaggedArray.GetLowerBound(0);i<=myJaggedArray.GetUpperBound(0);i++) 2. { 3. Console.WriteLine("item{0}",i); 4. for(int j=myJaggedArray[i].GetLowerBound(0);j<=myJaggedArray[i].GetUpperBound(0);j++) 5. { 6. Console.WriteLine(" item{0}{1}:{2}",i,j,myJaggedArray[i][j]); 7. } 8. }
2.使用GetValue/SetValue
GetValue方法定義如下:
public object GetValue(params int[]indices);
其中,多個int型參數indices的含義為下標。方法返回一個object對象,這是C#中所有對象的基類,使用多態性,它可以指向所有的C#對象。下面的代碼使用GetValue方法,循環輸出一個二維數組所有元素。
代碼5-3 使用GetValue輸出二維數組元素示例:Default.aspx.cs
1. //定義二維數組 2. string[,]myStrArr2=new string[,]{{"油","鹽"},{"《圍城》","《晨露》"},{"毛毛熊","Snoopy"}}; 3. //循環輸出 4. for(int i=myStrArr2.GetLowerBound(0);i<=myStrArr2.GetUpperBound(0);i++) 5. { 6. Console.WriteLine("item{0}",i); 7. for(int j=myStrArr2.GetLowerBound(1);j<=myStrArr2.GetUpperBound(1);j++) 8. { 9. Console.WriteLine(" item{0}{1}:{2}",i,j,myStrArr2.GetValue(i,j)); 10. } 11. }
SetValue的功能為數組的某個元素賦值,其定義及參數表同GetValue相似,不作贅述。
5.1.5 轉化元素類型
【本節示例參考:\示例代碼\C05\ Array_ConvertAll】
定義數組時,需要為其中的元素指定數據類型。有時候,在應用中需要重新轉化元素的類型,這可以使用Array對象的CovertAll方法實現:
public static TOutput[] ConvertAll<TInput,TOutput> (TInput[] array,Converter <TInput,TOutput> converter)
其中,類型參數TInput為源數組元素的類型,TOutput為目標數組元素的類型。參數array為要轉換為目標類型的從零開始的一維Array,converter為一個Converter對象,用于將每個元素從一種類型轉換為另一種類型。方法的返回值是目標類型的數組,包含從源數組轉換而來的元素。
Convert對象表示將對象從一種類型轉換為另一種類型的方法,其定義為:
public delegate TOutput Converter<TInput,TOutput>( TInput input)
其中,類型參數TInput為要轉換的對象的類型,TOutput為要將輸入對象轉換到的類型。參數input為要轉換的對象。方法的返回值為TOutput,它表示已轉換的TInput。
下面的代碼示例將一個元素類型為int的數組轉化為string類型。
代碼5-4 使用ConvertAll轉化數據元素類型:Default.aspx.cs
1. protected void Page_Load(object sender,EventArgs e) 2. { 3. //定義一個整數類型的數組,并輸出 4. int[]src={1,2,3}; 5. Page.Response.Write("<br/>"); 6. foreach(int i in src) 7. { 8. Page.Response.Write(i+":"+i.GetType()+"<br/>"); 9. } 10. 11. //將整數數組轉化為字符串類型,并輸出 12. string[]desc=Array.ConvertAll(src, 13. new Converter<int,string>(IntToString)); 14. Page.Response.Write("<br/>"); 15. foreach(string str in desc) 16. { 17. Page.Response.Write(str+":"+str.GetType()+"<br/>"); 18. } 19. } 20. 21. ///<summary> 22. ///Converter委托 23. ///</summary> 24. ///<param name="i">整數類型的數據</param> 25. ///<returns>由輸入轉化而成的字符串類型數據</returns> 26. public static string IntToString(int i) 27. { 28. return i.ToString(); 29. }
代碼首先在第3~9行定義了一個整數類型的數組src,并輸出其中的元素和數據類型;
第11~13行利用Array類的ConvertAll方法將src轉化為字符串類型,方法的第二個參數中使用了一個委托IntToString,其實現在第21~29行。這個委托功能簡單,將一個輸入的整數參數轉化為字符串,并返回。
第14~19行將轉化后的字符串數組輸出。程序運行結果如圖5.4所示。

圖5.4 使用ConvertAll方法轉化數組元素類型
5.1.6 遍歷數組元素
【本節示例參考:\示例代碼\C05\ Array_Traverse】
遍歷數組是指訪問數組中的全部元素一次并且僅一次。可以在遍歷的過程中完成許多操作,如查找等。有兩種方式可以遍歷整個數組,具體如下。
1.使用GetLowerBound/GetUpperBound方法
GetLowerBound方法可以獲取數組某一維上的最低下標,而GetUpperBound則可獲取其最高下標,利用這兩個參數和for語句,可以實現數組的遍歷。
public int GetLowerBound (int dimension) public int GetUpperBound (int dimension)
其中,參數dimension為需要獲取上下標的數組維度。
下面的示例實現了對二維數組的遍歷。
代碼5-5 利用for語句遍歷數組示例:Default.aspx.cs
1. //定義二維數組 2. string[,]myStrArr2=new string[,]{{"油","鹽"},{"《圍城》","《晨露》"},{"毛毛熊","Snoopy"}}; 3. //遍歷 4. for(int i=myStrArr2.GetLowerBound(0);i<=myStrArr2.GetUpperBound(0);i++) 5. { 6. for(int j=myStrArr2.GetLowerBound(1);j<=myStrArr2.GetUpperBound(1);j++) 7. { 8. //處理每一個元素 9. } 10. }
2.使用foreach
還可以使用更為簡便的方法來實現數組的遍歷,那就是foreach關鍵字,這種方法對于處理高維數組尤其方便。foreach語句格式如下:
foreach (data_typt item_name in arr_name) { //處理每一個元素 }
注意
foreach語句獲取的元素是最深層的元素,因此,無論處理幾維的數組,使用一層的foreach循環就可以了。
例如,下面的代碼實現同樣的二維數組遍歷。
代碼5-6 利用foreach遍歷數組示例:Default.aspx.cs
1. //定義二維數組 2. string[,]myStrArr2=new string[,]{{"油","鹽"},{"《圍城》","《晨露》"},{"毛毛熊","Snoopy"}}; 3. //遍歷 4. foreach(string item in myStrArr2) 5. { 6. { 7. //處理每一個元素 8. } 9. }
5.1.7 排序數組元素
【本節示例參考:\示例代碼\C05\ Array_Sort】
對數組進行排序是指按照一定的排序規則,如遞增或遞減規則,重新排列數組中的所有元素。可以使用Array類的Sort方法完成這個功能。Sort方法有多種重載方式,常用的形式如下:
public static void Sort(Array array);
其中,參數array為待排序的數組。下面的示例首先定義了一個數組,含有元素{5,4,3,2,1},然后利用Sort方法對其排序。
代碼5-7 利用Sort排序數組示例:Default.aspx.cs
1. ///<summary> 2. /// 利用Sort方法排序數組 3. ///</summary> 4. public void TestSort() 5. { 6. int[]myArr={5,4,3,2,1}; //定義數組 7. 8. //輸出原始數組:原始數組:5->4->3->2->1-> 9. Page.Response.Write("原始數組:"); 10. for(int i=0;i<myArr.Length;i++) 11. Page.Response.Write(myArr[i]+"->"); 12. 13. Array.Sort(myArr); //對數組排序 14. 15. Page.Response.Write("<br/>"); 16. 17. //并輸出排序后的數組:1->2->3->4->5-> 18. Page.Response.Write("排序以后數組:"); 19. for(int i=0;i<myArr.Length;i++) 20. Page.Response.Write(myArr[i]+"->"); 21. }
有時候需要進行所謂的關鍵字排序,例如,有兩個數組arrSid和arrSname,分別代表一組學生的學號和姓名,如果想要根據學號順序輸出姓名,或反之,都需要使用數組的排序操作,那么,如何把這兩個數組聯系在一起排序呢?這時就可以使用Sort的下面這種形式進行關鍵字排序。
public static void Sort(Array keys, Array items);
其中,參數keys代表關鍵字數組,而items代表另一個數組。利用Sort,下面的代碼可實現上述需求。
代碼5-8 利用Sort實現數組多關鍵字排序示例:Default.aspx.cs
1. ///<summary> 2. /// 利用Sort實現數組多關鍵字排序 3. ///</summary> 4. public void TestSortMultiKey() 5. { 6. //定義數組 7. int[]arrSid={5,4,3,2,1}; 8. string[]arrSname={"張三","李四","王五","麻子","淘氣"}; 9. 10. //輸出原始數組:原始數組:張三(5)->李四(4)->王五(3)->麻子(2)->淘氣(1)-> 11. Page.Response.Write("原始數組:"); 12. for(int i=0;i<arrSid.Length;i++) 13. Page.Response.Write(arrSname[i]+"("+arrSid[i]+"->"); 14. 15. //根據學號關鍵字排序 16. Array.Sort(arrSid,arrSname); 17. Page.Response.Write("<br/>"); 18. 19. //并輸出排序后的數組:淘氣(1)->麻子(2)->王五(3)->李四(4)->張三(5) 20. Page.Response.Write("排序以后數組:"); 21. for(int i=0;i<arrSid.Length;i++) 22. Page.Response.Write(arrSname[i]+"("+arrSid[i]+"->"); 23. }
示例非常簡單,輸出已經在注釋中給出,因此不作詳細說明。
5.1.8 查找數組元素
【本節示例參考:\示例代碼\C05\ Array_Search】
在數組中查找元素,可以有兩種解釋:一是從整個數組中尋找到與給定值相同的元素,可以使用Array類的BinarySearch方法完成這個功能;二是判斷數組中是否含有一個特定的元素,可以用Contains方法實現。
1.BinarySearch方法
BinarySearch使用二進制搜索算法在一維的排序Array中搜索值,注意必須是已經排序的數組。如果找到給定的值,則返回其下標;否則,返回一個負整數。其常用形式如下:
public static int BinarySearch(Array array,object value);
其中,參數array為待搜索的數組,value為要尋找的元素值。下面的示例首先定義了一個數組,含有元素{5,4,3,2,1},然后利用BinarySearch方法返回其中的元素3的下標(2)。
代碼5-9 利用BinarySearch搜索數組元素示例:Default.aspx.cs
1. ///<summary> 2. /// 利用BinarySearch二分搜索 3. ///</summary> 4. void TestBinarySearch() 5. { 6. //定義數組 7. int[]myArr={5,4,3,2,1}; 8. 9. //利用Sort排序 10. Array.Sort(myArr); 11. 12. //利用BinarySearch二分搜索 13. int target=3; 14. int result=Array.BinarySearch(myArr,target);//2 15. Page.Response.Write(target+"的下標為"+result+"<br/>"); //2 16. }
2.Contains方法
Contains方法可以確定某個特定值是否包含在數組中,返回一個bool值。Array類的這個方法實際上是對IList接口中方法的實現,其常用形式為:
bool IList.Contains(object value);
其中,參數value代表所要驗證的元素值,下面的示例判斷學生數組arrSname中是否包含“王五”。
代碼5-10 利用Contains判斷數組是否包含某個元素:Default.aspx.cs
1. ///<summary> 2. /// 利用Contains方法檢查是否包含某個元素 3. ///</summary> 4. void TestContains() 5. { 6. //定義數組 7. string[]arrSname={"張三","李四","王五","麻子","淘氣"}; 8. 9. //判斷是否含有某值 10. string target="王五"; 11. bool result=((System.Collections.IList)arrSname).Contains(target); 12. Page.Response.Write("是否包含"+target+"="+result+"<br/>"); //true 13. }
可以看到,在使用Contains方法時,需要首先將數組轉換為IList(隊列集合)對象。這是因為,本質上,數組是一種特殊的集合對象,因此可以把它轉換為一個集合對象。對于集合,將在下一章中對其進行詳細的討論。
5.1.9 反轉數組元素
【本節示例參考:\示例代碼\C05\ Array_Reverse】
反轉數組是指將一維數組中的全部或部分元素的順序,按照其逆序重新排列。可以使用Array類的Reverse靜態方法完成這個功能。其常用的形式為:
public static int Reverse(Array array); public static int Reverse(Array array,int index, int length);
其中,參數index指定所要反轉元素的起始下標,而length指定所要反轉的元素個數。
第一個重載形式可以反轉整個數組元素,參數array為待反轉的數組。下面的示例首先定義了一個數組,含有元素{5,4,3,2,1},然后利用Reverse方法進行反轉。
代碼5-11 利用Reverse反轉數組示例:Default.aspx.cs
1. ///<summary> 2. /// 利用Reverse方法反轉數組 3. ///</summary> 4. public void TestReverse() 5. { 6. //定義數組 7. int[]myArr={5,4,3,2,1}; 8. 9. //輸出原始數組:原始數組:5->4->3->2->1-> 10. Page.Response.Write("原始數組:"); 11. for(int i=0;i<myArr.Length;i++) 12. Page.Response.Write(myArr[i]+","); 13. 14. Page.Response.Write("<br/>"); 15. 16. //對數組反轉 17. Array.Reverse(myArr); 18. 19. //并輸出反轉后的數組:1->2->3->4->5-> 20. Page.Response.Write("反轉以后數組:"); 21. for(int i=0;i<myArr.Length;i++) 22. Page.Response.Write(myArr[i]+","); 23. }
5.1.10 復制數組
【本節示例參考:\示例代碼\C05\ Array_Copy】
復制數組可以得到一個和原數組完全一樣的新數組,可以用Array的Copy或CopyTo方法來實現。
1.Copy方法
在使用Copy方法進行數組復制操作之前,必須首先為新的數組分配空間,然后再通過復制操作向新的數組空間中填入元素值。靜態方法Copy的常用重載形式為:
public static void Copy(Array sourceArray,Array destinationArray,int length);
其中,參數sourceArray為源數組,destinationArray為目標數組,而length為要復制的元素數目,默認的復制操作從第一個元素開始。下面的示例首先定義了一個數組,含有元素{5,4,3,2,1},然后利用Copy方法獲取一個新的數組,包含了源數組的前3項。
代碼5-12 利用Copy復制數組示例:Default.aspx.cs
1. ///<summary> 2. /// 利用Copy靜態方法復制數組 3. ///</summary> 4. public void TestCopy() 5. { 6. //定義數組 7. int[]myArr={5,4,3,2,1}; 8. 9. //輸出原始數組:原始數組:5,4,3,2,1, 10. Page.Response.Write("原始數組:"); 11. for(int i=0;i<myArr.Length;i++) 12. Page.Response.Write(myArr[i]+","); 13. Page.Response.Write("<br/>"); 14. 15. //復制數組 16. int[]newArr=new int[3]; 17. Array.Copy(myArr,newArr,3); 18. 19. //并輸出反復制的數組:5,4,3, 20. Page.Response.Write("復制數組:"); 21. for(int i=0;i<newArr.Length;i++) 22. Page.Response.Write(myArr[i]+","); 23. }
注意
在使用Copy進行復制數組之前,必須要首先定義一個新的數組,并對其分配空間。另外,還需保證所分配的空間足夠容納所要復制的元素。否則在復制時將出現異常:“System.ArgumentException:目標數組的長度不夠”。
2.CopyTo方法
CopyTo和Copy方法的功能類似,但它是一個實例方法,即需要在數組對象上引用。另外,它只能復制源數組所有的元素,并可以控制元素在目標數組中存放的起始位置。其常用重載形式為:
public virtual void CopyTo( Array array, int index);
其中,參數array代表所要復制的目標數組,index則表示開始復制的元素下標。下面的代碼實現對源數組的復制,并從第3個位置開始存放。
代碼5-13 利用CopyTo復制數組示例:Default.aspx.cs
1. ///<summary> 2. /// 利用CopyTo靜態方法復制數組 3. ///</summary> 4. public void TestCopyTo() 5. { 6. //定義數組 7. int[]myArr={5,4,3,2,1}; 8. 9. //輸出原始數組:原始數組:5,4,3,2,1, 10. Page.Response.Write("原始數組:"); 11. for(int i=0;i<myArr.Length;i++) 12. Page.Response.Write(myArr[i]+","); 13. Page.Response.Write("<br/>"); 14. 15. //復制數組 16. int[]newArr=new int[7]; 17. myArr.CopyTo(newArr,2); 18. 19. //并輸出復制的數組:0,0,5,4,3,2,1, 20. Page.Response.Write("復制數組:"); 21. for(int i=0;i<newArr.Length;i++) 22. Page.Response.Write(newArr[i]+","); 23. }
另外,從輸出結果還可以看到,在初始化int型數組時,其默認值為0。
5.2 集合
上面介紹的數組常常用來實現靜態的操作,即不改變其空間大小,如查找、遍歷等。數組也可以實現動態的操作,如插入、刪除等,但不推薦使用,而應盡量使用本節介紹的集合來代替。
5.2.1 什么是集合
集合是指一組類似的對象。在.NET中,任意類型的對象都可以放入一個集合中。.NET中的集合類存在于System.Collections命名空間中,其常用的類和結構如圖5.5所示。

圖5.5 System.Collections命名空間常用類和結構
從圖5.5中可以看出,System.Collections空間中的集合種類眾多,主要包括如下。
1.一般集合
一般集合是常見的集合數據結構,包括哈希表、隊列、堆棧、字典和列表等。對于這些集合的詳細含義,讀者可參考數據結構方面的資料,本書不作詳細介紹,簡單說明如下:
(1)列表(ArrayList):一個一維的動態數組,可以裝載一組相似的數據元素。
(2)隊列(Quene):先進先出的列表。
(3)堆棧(Stack):先進后出的列表。
(4)哈希表(Hashtable):集合中的每個元素都是一個<鍵(key),值(value)>對的列表。
(5)字典(DictionaryEntry):一個<鍵(key),值(value)>對。
2.專用集合
專用集合是具有特定用途的集合,如StringCollection類,其元素只能為String對象。
3.位集合
位集合的元素為位標志,每個元素都是一位,而不是一個對象,常用于操作數只包含1、0的集合。
5.2.2 列表類ArrayList
System.Collections.ArrayList類實現了可變大小的一維數組,其常用屬性和方法如圖5.6所示。

圖5.6 ArrayList類的屬性和方法
ArrayList類常用屬性和方法的簡單說明,如表5.2所示。
表5.2 ArrayList類常用屬性/方法說明

下面各節,將詳細介紹其中最常用的方法。
5.2.3 創建列表
【本節示例參考:\示例代碼\C05\ArrayList_Create】
利用ArrayList的構造函數來創建一個新的列表,常用的形式如下:
public ArrayList(); public ArrayList(int capacity);
參數capacity可以指定所創建列表的初始容量。如果不指定,則初始容量為.NET的默認值16。下面的代碼創建了兩個列表對象:
ArrayList arr1=new ArrayList(); ArrayList arr2=new ArrayList(100);
其中,arr1的初始容量為16,arr2為100。目前,兩者里面都是空的,沒有任何元素。隨著操作的進行,當列表中的元素達到其最大容量時,列表將自動將其容量增加1倍。另外,如果想要使用ArrayList,首先需要在代碼頭部引入命名空間:
using System.Collections;
5.2.4 遍歷列表
【本節示例參考:\示例代碼\C05\ArrayList_Traverse】
1.使用foreach語句
遍歷列表是指訪問列表中的所有元素一遍,可以使用foreach語句完成這個功能。下例使用foreach輸出列表arr1中的所有元素。
代碼5-14 使用foreach遍歷列表示例:Default.aspx.cs
1. ///<summary> 2. /// 利用Foreach語句遍歷集合 3. ///</summary> 4. void TestForeach() 5. { 6. ArrayList arr1=new ArrayList(); 7. 8. //循環添加元素0~9 9. for(int i=0;i<10;i++) 10. arr1.Add(i); 11. 12. //使用foreach遍歷數組,輸出所有元素 13. foreach(object item in arr1) 14. { 15. Page.Response.Write(item+"<br/>"); 16. } 17. }
2.使用GetEnumerator方法
除了foreach之外,還可以使用ArrayList的GetEnumerator方法實現列表的遍歷。其形式為:
public virtual IEnumerator GetEnumerator();
該方法返回整個ArrayList的枚舉對象IEnumerator。這個對象可以得到一個集合對象的所有元素,其最主要的屬性為Current,用于獲取集合中的當前元素。
主要的方法包括:
MoveNext:將枚舉推進到集合的下一個元素。在傳遞到集合的末尾之后,枚舉數放在集合中最后一個元素后面,且調用MoveNext會返回false。
Reset:將枚舉設置為其初始位置,該位置位于集合中第一個元素之前。
下面的代碼使用GetEnumerator實現上面foreach同樣的列表遍歷功能。
代碼5-15 使用GetEnumerator遍歷列表示例:Default.aspx.cs
1. ///<summary> 2. /// 利用GetEnumerator遍歷數組 3. ///</summary> 4. void TestGetEnumerator() 5. { 6. ArrayList arr1=new ArrayList(); 7. 8. //循環添加元素0~9 9. for(int i=0;i<10;i++) 10. arr1.Add(i); 11. 12. //使用GetEnumerator遍歷數組 13. System.Collections.IEnumerator enm=arr1.GetEnumerator(); 14. while(enm.MoveNext()) 15. { 16. Page.Response.Write(enm.Current+"<br/>"); 17. } 18. }
注意
最初,枚舉數被定位于集合中第一個元素的前面。此時,調用Current會引發異常。因此在讀取Current的值之前,必須調用MoveNext將枚舉數提前到集合的第一個元素。
5.2.5 添加元素
【本節示例參考:\示例代碼\C05\ArrayList_Add】
可以通過ArrayList的Add和AddRange方法,實現向一個列表中添加數據。兩者的區別在于:Add一次只能添加一個元素,而AddRange一次可以添加多個元素,這多個元素需要放在一個集合或數組中。兩者常用的形式如下:
public int Add(object value); public void AddRange(ICollection c);
下面的示例中,首先定義了一個列表arr1,然后使用Add方法,向arr1中添加了兩個元素,其中第一個為字符串對象“Hello”,第二個為一個整數對象1。
然后分別定義了兩個列表arr2、arr3,并分別使用Add和AddRange方法試圖將arr1中的所有數據都添加到arr2、arr3中。從結果中可以看出,只有使用AddRange才能實現這個目的,而使用Add方法則可以得到一個二維數組,第一維的元素為arr1。
代碼5-16 向ArrayList中添加元素示例:Default.aspx.cs
1. ArrayList arr1=new ArrayList(); 2. 3. //向arr1中添加一個字符串對象“Hello” 4. object item=new object(); 5. item="Hello"; 6. arr1.Add(item); //arr1:{"Hello"} 7. //向arr1中添加一個整數對象1 8. item=1; 9. arr1.Add(item); //arr1:{"Hello",1} 10. 11. //向另一個列表中添加arr1中的所有元素 12. ArrayList arr2=new ArrayList(); 13. arr2.Add(arr1); //arr2只有一個元素:{arr1}={{“Hello",1}} 14. 15. ArrayList arr3=new ArrayList(); 16. arr3.AddRange(arr1); //arr3:{"Hello",1}
Add和AddRange方法只能將元素添加到列表的末尾,如果想要在列表的任意位置添加元素,則需要使用Insert方法。
5.2.6 插入元素
【本節示例參考:\示例代碼\C05\ArrayList_Insert】
如前面所述,使用ArrayList的Insert和InsertRange方法,可以實現向一個列表中的任意位置添加數據。這兩者的區別同Add與AddRange的區別類似,Insert一次只能添加一個元素,而InsertRange一次可以添加某個集合中的多個元素。兩者常用的形式分別如下:
public int Insert(int index, object value); public void InsertRange(int index, ICollection c);
其中,參數index指定元素所要插入的位置,從0開始索引。下面的示例中,首先定義了一個列表arr1,然后使用Add方法,向arr1中添加了兩個元素,其中第1個為字符串對象“Hello”,第2個為一個整數對象1。然后,在兩者之間插入一個整數型數組元素{1,2,3}。
代碼5-17 向ArrayList中插入元素示例:Default.aspx.cs
1. ArrayList arr1=new ArrayList(); 2. 3. //向arr1中添加一個字符串對象“Hello” 4. object item=new object(); 5. item="Hello"; 6. arr1.Add(item); //arr1:{"Hello"} 7. //向arr1中添加一個整數對象1 8. item=1; 9. arr1.Add(item); //arr1:{"Hello",1} 10. 11. //插入一個數組元素arr2 12. int[]arr2=new int[]{1,2,3}; 13. arr1.Insert(1,arr2);
使用Insert方法把arr2插入在位置1后,arr1的結果如圖5.7所示。可以看出,arr1的長度為3,下標1處的元素為數組arr2,里面又包括了3個整數元素。

圖5.7 使用Insert后的arr1結果
如果把代碼第13行的Insert改為InsertRange,則將會把數組中的所有元素插入到arr1中,而不是數組本身,結果將如圖5.8所示。

圖5.8 使用InsertRange后的arr1結果
5.2.7 刪除元素
【本節示例參考:\示例代碼\C05\ArrayList_Delete】
ArrayList中支持刪除元素的方法分別如下:
public void Remove(object obj);
該方法用于刪除數組中特定對象obj的第一個匹配項。參數obj為要從ArrayList移除的Object。
public void RemoveAt(int index);
該方法用于移除ArrayList的指定索引處的元素。參數index為要移除的元素的從0開始的索引。
public void RemoveRange(int index,int count);
該方法用于從ArrayList中移除一定范圍的元素。參數index為要移除元素的起始索引(從0開始計數),參數count為要移除的元素數。
下面的示例中,首先定義了一個列表arr1,使用Add方法添加進10個元素(整數0~9),然后分別使用上面的3種方法,分別刪除掉元素3、元素5,以及元素{7, 8, 9}。
代碼5-18 從ArrayList中刪除元素示例:Default.aspx.cs
1. ArrayList arr1=new ArrayList(); 2. 3. //循環添加元素1~9 4. for(int i=0;i<10;i++) 5. arr1.Add(i); 6. 7. //使用Remove(),刪除掉3 8. arr1.Remove(3); 9. 10. //使用RemoveAt(),刪除掉5,注意:此時,元素5的下標為? 11. arr1.RemoveAt(4); 12. 13. //使用RemoveRange(),一次刪除掉7、8、9,注意,元素7的下標為? 14. arr1.RemoveRange(5,3);
5.2.8 簡單排序
【本節示例參考:\示例代碼\C05\ArrayList_SimpleSort】
對集合元素進行排序也是非常重要的操作之一,許多進一步的操作可以在排序的基礎上進行,例如二分查找等。ArrayList使用Sort方法來實現排序功能,其常用形式如下:
public virtual void Sort();
該形式可以對整個ArrayList中的元素進行排序。
下例簡單使用了無參數的Sort方法,對一個列表中的元素進行排序。這時,默認的排序策略為非遞減排序。
代碼5-19 使用Sort方法對列表排序示例:Default.aspx.cs
1. ///<summary> 2. /// 利用Sort方法進行排序 3. ///</summary> 4. public void TestSort() 5. { 6. ArrayList arr1=new ArrayList(); 7. 8. //循環添加元素5~1 9. for(int i=5;i>0;i--) //arr1:{5,4,3,2,1} 10. arr1.Add(i); 11. 12. //使用Sort(),實現簡單的非遞減排序 13. arr1.Sort(); //arr1:{1,2,3,4,5} 14. }
5.2.9 復雜排序
【本節示例參考:\示例代碼\C05\ArrayList_ComplexSort】
有時需要進行更為復雜的排序操作。例如,利用逆向比較策略(即1>2策略)進行排序等。這時需要首先利用IComparer接口實現一個具體的比較策略,然后再利用Sort方法進行排序,所用到的Sort方法形式如下:
public virtual void Sort(IComparer comparer);
該方法使用指定的比較策略對整個ArrayList中的元素進行排序。其中,參數comparer為比較元素時要使用的比較策略,是對接口IComparer的實現。
下例中,首先利用IComparer接口方法,實現一個逆向比較策略類。IComparer中包含一個Compare接口,需要對這個接口進行實現,返回一個整數值來表示兩個待比較對象x、y的大小關系。正數表示x>y;0表示x= =y;負數表示x<y。
代碼5-20 使用Compare方法實現逆比較示例:Default.aspx.cs
1. public class myReverserClass:IComparer 2. { 3. //實現IComparer接口的Compare方法,實現逆比較 4. int IComparer.Compare(Object x,Object y) 5. { 6. if(Convert.ToInt32(x)>Convert.ToInt32(y)) 7. return-1; 8. else if(Convert.ToInt32(x)==Convert.ToInt32(y)) 9. return 0; 10. else 11. return 1; 12. } 13. }
然后,便可以使用這個比較器,結合Sort方法進行逆排序,如下所示:
1. ArrayList arr1=new ArrayList(); 2. 3. //循環添加元素1~5 4. for(int i=1;i<6;i++) //arr1:{1,2,3,4,5} 5. arr1.Add(i); 6. 7. //使用逆比較策略,實現逆排序 8. IComparer myComparer=new myCompareReverse(); 9. arr1.Sort(myComparer); //arr1:{5,4,3,2,1}
另外,當需要對數組中的某一部分進行排序時,可以使用Sort的如下形式:
public virtual void Sort(int index,int count,IComparer comparer);
這里使用指定的比較策略對部分ArrayList中的元素進行排序。參數index為要排序的起始索引,count為要排序的范圍的長度,comparer為比較元素時要使用的比較策略,是對接口IComparer的實現。
5.2.10 查找元素
【本節示例參考:\示例代碼\C05\ArrayList_Search】
在集合中對特定元素的查找也是常用的操作之一,ArrayList提供了二分查找的方法BinarySearch,可以在O(logn)時間復雜度內完成查找,其常用形式如下:
public virtual int BinarySearch(object value);
在整個已排序的ArrayList中搜索元素,并返回該元素從0開始的索引。
代碼5-21首先使用默認的非遞增簡單排序方法對一個列表中的元素進行排序,然后使用BinarySearch方法搜索其中的特定元素,并輸出其索引。
代碼5-21 使用BinarySearch方法查找元素示例:Default.aspx.cs
1. ArrayList arr1=new ArrayList(); 2. 3. //循環添加元素10~1 4. for(int i=0;i<10;i++) 5. arr1.Add(10-i); 6. 7. //使用BinarySearch,查找元素7,并輸出 8. arr1.Sort(); //首先需要進行排序 9. int idx=arr1.BinarySearch(7); 10. Page.Response.Write("arr["+idx+"]=7");//arr[6]=7
如果使用指定的排序策略對集合中的元素進行排序之后,相應地,也可以使用同樣的排序策略,結合BinarySearch方法實現元素的查找。這時,其形式為:
public virtual int BinarySearch(object value,IComparer comparer);
此時,將使用指定的比較器在整個已排序的ArrayList中搜索元素,并返回該元素從0開始的索引。參數value為要查找的元素,而Icomparer為指定的比較策略,可參考5.2.9節相關內容。
5.3 隊列
上一節介紹了集合中的列表類ArrayList,下面介紹一個特殊的列表——隊列。
5.3.1 什么是隊列
隊列實際上是一種特殊的列表。它對列表的操作進行了限制,要求列表中的元素必須滿足先進先出的原則,這類似于現實生活中的排隊,如圖5.9所示。

圖5.9 隊列示意圖
說明
隊列及后面所介紹的堆棧等,都是非常重要的數據結構,掌握它們的思想及典型應用,在程序設計中具有重要的作用。但是,本書將不從程序設計的層次討論Queue類的應用,而僅從集合操作的層次進行討論。如果讀者不了解數據結構的內容,筆者強烈建議補充一下這方面的知識。
5.3.2 隊列類Queue
Queue類常用屬性和方法如圖5.10所示。

圖5.10 Queue類的屬性和方法
Queue類最主要的方法為入隊操作Enqueue和出隊操作Dequeue,分別完成在隊列尾的添加新元素操作和在隊列頭的刪除元素操作。其他Queue支持的方法,如遍歷、清空等,和ArrayList類似,這里不作贅述。
5.3.3 創建隊列
【本節示例參考:\示例代碼\C05\Queue_Create】
利用Queue的構造函數來創建一個新的隊列,常用的形式包括:
public Queue (); public Queue (int capacity); public Queue( int capacity, float growFactor);
參數capacity可以指定所創建列表的初始容量,如果不指定,則初始容量為.NET的默認值32。而參數growFactor則指定當隊列滿后容量的增長率,新容量等于當前容量與growFactor的乘積,默認值為2.0。下面的代碼創建了3個隊列對象:
Queue queue1=new Queue(); Queue queue2=new Queue(100); Queue queue3=new Queue(100,1.5f);
其中,queue1的初始容量為32,queue2為100。目前,兩者里面都是空的,沒有任何元素。隨著操作的進行,當列表中的元素達到其最大容量時,列表將自動增長至200。而對于queue3,其初始容量為100,當列表中的元素達到其最大容量時,列表將自動增長至150。
與ArrayList一樣,如果想要使用Queue,首先需要在代碼頭部引入命名空間:
using System.Collections;
5.3.4 元素入隊
【本節示例參考:\示例代碼\C05\Queue_Enqueue】
可以通過Queue的Enqueue,實現一個元素的入隊操作,其形式如下:
public virtual void Enqueue(object obj);
下面的示例中,首先定義了一個隊列queue1,然后使用Enqueue方法,向其中添加了3個元素,其中第1個為字符串對象“Hello”,第2個為一個整數對象1,第3個為整數型數組arr1。
代碼5-22 使用Enqueue元素入隊示例:Default.aspx.cs
1. Queue queue1=new Queue(); 2. 3. //字符串:"Hello"入隊 4. object item=new object(); 5. item="Hello"; 6. queue1.Enqueue(item); 7. //整數:1入隊 8. item=1; 9. queue1.Enqueue(item); 10. //數組:arr1={1,2,3}入隊 11. int[]arr1=new int[]{1,2,3}; 12. queue1.Enqueue(arr1);
隊列中的元素如圖5.11所示。

圖5.11 隊列入隊示例
5.3.5 元素出隊
【本節示例參考:\示例代碼\C05\Queue_Dequeue】
與Enqueue相反,可以通過Queue的Dequeue實現一個元素的出隊操作,其形式如下:
public virtual object Dequeue();
下面的示例中,首先定義了一個隊列queue1,然后使用Enqueue方法,依次入隊3個元素。然后再使用Dequeue方法,輸出所有的元素。注意,這個輸出的順序和入隊的順序是一致的。
代碼5-23 使用Dequeue元素出隊示例:Default.aspx.cs
1. Queue queue1=new Queue(); 2. 3. //依次入隊:"Hello"、1、{1,2,3} 4. queue1.Enqueue("Hello"); 5. queue1.Enqueue(1); 6. queue1.Enqueue(new int[]{1,2,3}); 7. 8. //通過出隊操作,輸出隊列中的所有元素,這個順序,與入隊的順序一致 9. object outItem=new object(); 10. while(queue1.Count>0) 11. { 12. outItem=queue1.Dequeue(); 13. Page.Response.Write(outItem+"<br/>"); 14. }
此時,代碼輸出的結果應與圖5.11所示相同。
5.4 堆棧
上面介紹了具有先入先出特征的隊列,下面繼續介紹另一種具有先入后出特征的集合對象:堆棧。
5.4.1 什么是堆棧
與Queue一樣,System.Collections.Stack實際上也是一種操作受限的列表,它要求列表中的元素必須滿足先進后出的原則,如圖5.12所示。

圖5.12 堆棧示意圖
5.4.2 堆棧類Stack
Stack類常用屬性和方法如圖5.13所示。其中,最主要的方法為入棧操作Push和出棧操作Pop,分別完成在堆棧的頂部添加和刪除元素的操作。其他Stack支持的方法,如遍歷、清空等,和ArrayList類似。

圖5.13 Stack類的屬性和方法
5.4.3 創建堆棧
【本節示例參考:\示例代碼\C05\Stack_Create】
利用Stack的構造函數來創建一個新的堆棧,常用的形式包括:
public Stack (); public Stack (int capacity);
參數capacity可以指定所創建列表的初始容量。如果不指定,則初始容量為.NET的默認值10。當堆棧中的元素達到其最大容量時,其容量將自動增加一倍。下面的代碼創建了兩個堆棧對象:
Stack Stack1=new Stack(); Stack Stack2=new Stack(100);
其中,Stack1的初始容量為10,Stack2為100。同ArrayList、Queue一樣,如果想要使用Stack,首先需要在代碼頭部引入命名空間:
using System.Collections;
5.4.4 元素入棧
【本節示例參考:\示例代碼\C05\Stack_Push】
可以通過Stack的Push,實現一個元素的入棧操作,其形式如下:
public virtual void Push(object obj);
下面的示例中,首先定義了一個堆棧Stack1,然后使用Push方法,向其中添加了3個元素,其中第1個為字符串對象“Hello”,第2個為一個整數對象1,第3個為整數型數組arr1。
代碼5-24 使用Push元素入棧示例:Default.aspx.cs
1. Stack stack1=new Stack(); 2. 3. //字符串:"Hello"入棧 4. object item=new object(); 5. item="Hello"; 6. stack1.Push(item); 7. //整數:1入棧 8. item=1; 9. stack1.Push(item); 10. //數組:arr1={1,2,3}入棧 11. int[]arr1=new int[]{1,2,3}; 12. stack1.Push(arr1);
堆棧中的元素是如何存放的?把堆棧假想為一個盛放油餅的框子,那么“Hello”這張餅被壓在最下面,整數1在中間,數組{1,2,3}在框子的最上面,效果如圖5.14所示。

圖5.14 堆棧出棧示例
5.4.5 元素出棧
【本節示例參考:\示例代碼\C05\Stack_Pop】
與Push相反,可以通過Stack的Pop實現一個元素的出棧操作,其形式如下:
public virtual object Pop();
在下面的示例中,首先定義了一個堆棧Stack1,然后使用Push方法,依次入隊3個元素。再使用Pop方法,輸出所有的元素。這個輸出的順序和入隊的順序是相反的。
代碼5-25 使用Pop元素出棧示例:Default.aspx.cs
1. Stack stack1=new Stack(); 2. 3. //依次入棧:"Hello"、1、{1,2,3} 4. stack1.Push("Hello"); 5. stack1.Push(1); 6. stack1.Push(new int[]{1,2,3}); 7. 8. //利用Pop方法,循環輸出stack1中所有的元素,注意,輸出的順序與入棧的順序是相反的 9. object outItem=new object(); 10. while(stack1.Count>0) 11. { 12. outItem=stack1.Pop(); 13. Response.Write(outItem+"<br/>"); 14. }
此時,代碼輸出的結果應與圖5.14相同,就像從框子中把油餅逐個取出一樣。
承上啟下
■ 學完本章后,讀者需要回答:
1.什么是數組?.NET基礎類庫中的System.Array和數組是什么關系?
2.能夠使用數組完成以下操作。
(1)元素訪問:GetValue
(2)轉化類型:ConvertAll
(3)遍歷數組:foreach
(4)排序數組:Sort
(5)查找元素:BinarySearch/Contains
(6)反轉數組:Reverse
(7)復制數組:Copy/CopyTo
3.C#中的集合命名空間包括哪些類?
4.能夠使用ArrayList類完成以下操作。
(1)添加元素:Add/AddRange
(2)插入元素:Insert/InsertRange
(3)刪除元素:Remove/RemoveAt/RemoveRange
(4)排序元素:Sort
(5)查找元素:BinarySeach
5.Queue類和Stack類的特點是什么?如何實現隊列及堆棧的以下操作。
(1)入隊:Enqueue
(2)出隊:Dequeue
(3)入棧:Push
(4)出棧:Pop
■ 在下一章中,讀者將會了解:
1.利用VS.NET開發環境尋找語法錯誤和邏輯錯誤的技術。
2.利用Exception類和try-catch來捕捉程序異常的技術。
3.異常執行的先后順序。