這一節我們來學習集合,什麼是集合呢? 集合就如同數組,用來存儲和管理一組特定類型的數據對象,除了基本的數據處理功能,集合直 接提供了各種數據結構及算法的實現,如隊列、鏈表、排序等,可以讓你輕易地完成復雜的數據操作。在使用數組和集合時要先加入 system.collections命名空間,它提供了支持各種類型集合的接口及類。集合本身上也是一種類型,基本上可以將其作為用來存儲一組數據對 象的容器,由於c#面向對象的特性,管理數據對象的集合同樣被實現成為對象,而存儲在集合中的數據對象則被稱為集合元素。這裡提到了接 口這個概念,它也是面向對象編程進化的重要標准,我們在這裡不做過多的講解,先注重學習集合中的對象及其使用就可以了,下面我們來學 習第一種集合:
動態數組ArrayList.ArrayList類提供了繼承了IList接口。什麼是繼承呢?這也是面向對象語言的重要特點之一,現在你們先把它理解為, 如果一個對象繼承了類或接口,那麼它也具有了這個類和接口中的方法、屬性,可以用這些繼承的方法和屬性來做相應的操作,比如:數組增 加元素沒有Add()方法,但是動態數組ArrayList繼承了一個增加元素有Add()方法的接口,那麼當它要增加元素的時候,不僅可以用索引, 也可以用繼承下來的Add()方法了。隨著學習的深入,我會給大家再具體講解繼承的概念和使用繼承的好處。那麼下面讓我們來看看動態數組 所繼承的這個接口IList它有什麼特性呢?
Ilist接口:定義了利用索引訪問集合對象的方法,還繼承了ICollection和IEnumerable接口,除實現了接口原有的方法成員外,其本身也 定義多個專門的方法成員,例如新增、移除、在指定位置插入元素或是返回特定元素在集合中所在的位置索引,這些方法主要為集合對象提供 類似數組的元素訪問功能。
ILsit接口成員:add、insert、RemoveAt、Remove、contains、Clear、indexof方法,它最大的特色在於提供類 似數組索引的訪問機制。
ArrayList對象是較為復雜的數組。我們可以將它看為擴充了功能的數組,但ArrayList並不等同於數組,與數組相比,它以下功能和區別是 :
1. 數組的容量是固定的,但ArrayList的容量可以根據需要自動擴充。當我們修改了ArrayList的容量時,則可以自動進行內存重新分配和 元素復制,比如往1號索引位插入n個元素,插入後,元素的索引依次向後n個位置排列,它是動態版本的數組類型。
2.ArrayList提供添加、插入或移除某一范圍元素的方法。但是在數組中,只能一次獲取或設置一個元素的值,如利用索引賦值。
3.ArrayList只有一維,而數組可以是多維。
4.ArrayList可以存放任何數據類型的數據,而數組只能存儲同種數據類型的數據。
這種可以存放任何數據類型的機制,我們稱為裝箱,本節課的後半部分我會講解到,現在大家只需要記住,無論是什麼數據類型,只要添加 到動態數組中,都將轉變為Object數據類型,所以在遍歷Arraylist時,要定義一個Object類型的變量,用來接收遍歷它的每個項的值。
如何聲明一個動態數組呢?
ArrayList AL=new ArrayList( Capacity );//初始容量capacity也是可以不寫的
原因就是即使不在初識化確定容量,容量不夠的時候,會自動的按倍數作擴充。
接下來我們來看一下動態數組的常用屬性
Capacity 獲取或設置ArrayList可包含的元素數。
Count 獲取ArrayList中實際包含的元素數。
IsReadOnly 獲取一個值,該值表示ArrayList是否為只讀。
Item 獲取或設置指定索引處的元素。
動態數組的常用方法
增加元素-AL.Add(value);利用Add方法增加集合元素值
修改元素-AL[Index]=value;利用索引的方式修改元素的值
插入元素-AL.Insert(Index,value);將元素的值value,插入到第Index位置 。
刪除元素-AL.Clear(); 全部刪除集合中的元素
AL.Remove(value);按照集合元素值刪除元素
AL.RemoveAt(Index);按照集合的元素索引刪除元素
縮減容量-AL.TrimToSize();將集合的容量減少到實際元素個數的大小
在執行刪除操作後,要養成良好的縮減容量的習慣,節省內存空間,提高性能。
查找元素-除了按數組的索引查找外,還可以用 AL.Contains(value);按照元素值查找集合,如果包含便返回True,不包含時返回False。
得到類型--AL[index].GetType() 可以得到在index索引位的元素的數據類型
元素排序-- AL.Sort();
元素反轉-- AL.Reverse();
下面的例子我會給你們分部的演示出以上方法及屬性,運行結果我會用圖片的形式顯示出來,幫助你們理解動態數組是使用。
添加元素和得到數據類型
1 ArrayList AL = new ArrayList();
2 Console.WriteLine("容量: " + AL.Capacity);
3 Console.WriteLine("給動態數組添加元素:");
4 AL.Add("Hello");
5 Console.WriteLine("容量: " + AL.Capacity);
6 AL.Add(" World");
7 foreach (Object obj in AL)
8 {
9 Console.Write(obj);
10 }
11 Console.WriteLine();
12 Console.WriteLine("個數:" + AL.Count);
13 Console.WriteLine ("容量: " + AL.Capacity);
14 Console.WriteLine("第1個元素的數據類型為:" + AL[0].GetType ());
插入
1 AL.Insert(1, " c#");
2 Console.Write("在索引值為1的地方插入 ");
3 foreach (Object obj in AL)
4 { Console.Write(obj); }
5 Console.WriteLine();
6 Console.WriteLine("個數:" + AL.Count);
7 Console.WriteLine("容量: " + AL.Capacity);
結果容量: 0
給動態數組添加元素:
容量: 4
Hello World
個數:2
容量: 4
第1個元素的數據類型為:System.String
在 索引值為1的地方插入 Hello c# World
個數:3
容量: 4
請按任意鍵繼續. . .
擴充容量
1 AL.Add("。");
2 Console.WriteLine("容量。: " + AL.Capacity);
3 AL.Add("---");
4 Console.WriteLine("容量---: " + AL.Capacity);
5
6 foreach (Object obj in AL)
7 { Console.Write(obj); }
結果容量: 0
給動態數組添加元素:
容量: 4
Hello World
個數:2
容量: 4
第1個元素的數據類型為:System.String
在 索引值為1的地方插入 Hello c# World
個數:3
容量: 4
容量。: 4
容量---: 8
Hello c# World。---
請按任意鍵繼續. . .
根據索引找到元素
1 Console.WriteLine("\n3號索引的:" + AL[3]);
2 Console.WriteLine("集合中是否包含?: " + AL.Contains("?"));
3 Console.WriteLine("經過之前操作後的集合元素:");
4 foreach (Object obj in AL)
5 { Console.Write(obj); }
6
7 Console.WriteLine("\n個 數:" + AL.Count);
8 Console.WriteLine("容量: " + AL.Capacity);
結果
容量: 0
給動態數組添加元素:
容量: 4
Hello World
個數:2
容量: 4
第1個元素的數據類型為:System.String
在索引 值為1的地方插入 Hello c# World
個數:3
容量: 4
容量。: 4
容量---: 8
Hello c# World。---
3號索引的:。
數組中是否 包含?:False
經過之前操作後的數組元素:
Hello c# World。---
個數:5
容量: 8
請按任意鍵繼續. . .
按值刪
1 AL.Remove("。");
2 AL.Remove("?");
3
4 Console.WriteLine("沒有?個數只減少1個容量不變");
5 foreach (Object obj in AL)
6 { Console.Write(obj); }
7 Console.WriteLine();
8 Console.WriteLine("個數:" + AL.Count);
9 Console.WriteLine("容量: " + AL.Capacity);
結果
容量: 0
給動態數組添加元素:
容量: 4
Hello World
個數:2
容量: 4
第1個元素的數據類型為:System.String
在索引值為1的地方插入 Hello c# World
個數:3
容量: 4
容量。: 4
容量---: 8
Hello c# World。---
3號索引的:。
數組 中是否包含?:False
經過之前操作後的數組元素:
Hello c# World。---
個數:5
容量: 8
沒有?個數只減少1個容量不變
Hello c# World---
個數:4
容量: 8
請按任意鍵繼續. . .
按照索引刪 1 AL.RemoveAt(3);
2 Console.WriteLine("移除3號索引位的元素:");
3 foreach (Object obj in AL)
4 { Console.Write(obj); }
5 Console.WriteLine();
6 Console.WriteLine("個數:" + AL.Count);
7 Console.WriteLine("容量: " + AL.Capacity);
8
9
結果
容量: 0
給動態數組添加元素:
容量: 4
Hello World
個數:2
容量: 4
第1個元素的數據類型為:System.String
在索引 值為1的地方插入 Hello c# World
個數:3
容量: 4
容量。: 4
容量---: 8
Hello c# World。---
3號索引的:。
數組中是否 包含?:False
經過之前操作後的數組元素:
Hello c# World。---
個數:5
容量: 8
沒有?個數只減少1個容量不變
Hello c# World---
個數:4
容量: 8
移除3號索引位的元素:
Hello c# World
個數:3
容量: 8
請按任意鍵繼續. . .
1 AL.TrimToSize(); //縮減容量
2 Console.WriteLine("實際容量: " + AL.Capacity);
結果容量: 0
給動態數組添加元素:
容量: 4
Hello World
個數:2
容量: 4
第1個元素的數據類型為:System.String
在 索引值為1的地方插入 Hello c# World
個數:3
容量: 4
容量。: 4
容量---: 8
Hello c# World。---
3號索引的:。
數組中 是否包含?:False
經過之前操作後的數組元素:
Hello c# World。---
個數:5
容量: 8
沒有?個數只減少1個容量不變
Hello c# World---
個數:4
容量: 8
移除3號索引位的元素:
Hello c# World
個數:3
容量: 8
實際容量: 3
請按任意鍵繼續. . .
1 AL.Clear();
2 Console.WriteLine("清除全部元素後:");
3 Console.WriteLine("個數:" + AL.Count);
4 Console.WriteLine("容量: " + AL.Capacity);
Title容量: 0
給動態數組添加元素:
容量: 4
Hello World
個數:2
容量: 4
第1個元素的數據類型為:System.String
在 索引值為1的地方插入 Hello c# World
個數:3
容量: 4
容量。: 4
容量---: 8
Hello c# World。---
3號索引的:。
數組中 是否包含?:False
經過之前操作後的數組元素:
Hello c# World。---
個數:5
容量: 8
沒有?個數只減少1個容量不變
Hello c# World---
個數:4
容量: 8
移除3號索引位的元素:
Hello c# World
個數:3
容量: 8
實際容量: 3
清除全部元素後:
個數:0
容量: 3
請按任意鍵繼續. . .
通過以上的例子你們應該已經理解集合的方法,我們再來總結一下集合ArrayList相比數組有什麼好處?主要是它可以根據使用大小按需動 態增加,不用受事先設置大小的控制,還有就是可以隨意的添加、插入或移除某一范圍元素,比數組要方便。但是它也有不足,ArrayList 不 管對象是什麼類型都會添加到集合j中,在編譯時都是沒有問題的,但是在遍歷的時候,為防止集合中元素的類型不一致,所以最好使用object 類型來接收遍歷j的元素,如foreach(object i in j)這樣就能減少錯誤,可能同學們會想,用object類型我們記住了,怎麼就成弊端的呢?
這裡我們就要學到另一個知識點,就是裝箱和拆箱。所謂裝箱就是把值類型打包成object引用類型的一個實例中,也就是說在進行裝箱的時 候,必須分配並構造一個全新的對象。而拆箱就是指從對象中提取值類型,將object類型強制轉換為原類型。
比如:
ArrayList j=new ArrayList();
j.Add(123);
j.Add("123");
在添加時,ArrayList是會隱式的將整形的123 進行如下裝箱操作: int i=123; object o=(object)i;也就是說存進j的元素都將變成 object類型
而在使用這個整形的123時,ArrayList又會進行如下的拆箱操作: o=123; i=(int)o; 也就是將o再強制轉換成原來的類型表現出去
想想這將是很大的性能消耗,需要進行大量的計算,至於怎麼記住裝箱拆箱,我們就把這個過程想象成現實生活中,你買了很多中水果(元 素),為了方便搬運,我們把他們都放到一個大盒子(集合對象)裡,但是因為有榴蓮,我們又得把榴蓮(值類型元素)單獨包裝好(裝箱過 程)再放到盒子裡,到了家後,我們要打開盒子取出水果,在拿到榴蓮時,要想見到真正的榴蓮,我們就的把包裝去掉(拆箱),我們馬上就 聞到了榴蓮那獨有的味道了(變回原類型)。哎!這個過程多麻煩呀!分了這麼多步,在C#2.0出來後,就推出了新的技術來解決這個問題,那 就是泛型,以後的章節我會講解這個新特性。
下面我們來講一下ArrayList向數組的互換。
將數組轉換成動態數組
1 //數組轉換成動態數組
2 int[] c = { 1, 2 };
3 //1.利用for循環添加到動態數組中
4 ArrayList d = new ArrayList();
5 for (int i = 0; i < c.Length; i++)
6 {
7 d.Add(c[i]);
8 }
9 //2.使用Adapter方法,將數組打包到動態數組中
10 ArrayList b = ArrayList.Adapter(c);
11 foreach (int i in b)
12 {
13 Console.WriteLine(i);
14 }
動態數組轉換成數組
1 //動態數組轉換成數組
2 ArrayList arl = new ArrayList(4);
3 arl.Add ("W");
4 arl.Add("h");
5 arl.Add("y");
6 //arl.Add(1);
7 //只能將包含同一種數據類型集合轉換成這種類型的數組
8 //如果ar1中a添加了元素整型 1,將無法被轉換為string類型的數組
9 string[] ar = new string[arl.Count];
10
11 //使用typeof (string)限定放入數組ar中的數據都是string類型,
12 //同時再將arl強制轉換string類型數組,賦給數組ar
13 ar = (string[])arl.ToArray(typeof(string));
14 foreach (string a in ar)
15 {
16 Console.Write(a);
17 }
例題:
1 ArrayList al = new ArrayList();//定義一個動態數組
2 Random rd = new Random();//定義一個隨機數對象
3 do
4 {
5 int i = rd.Next(1, 36);//在1-35中產生隨機數
6 if (!al.Contains (i))//如果al不包含隨機數i
7 al.Add(i);//我們就把i添加到動態數組中
8 } while (al.Count < 7);//當動態數組的長度是7的時候,我們停止添加。
9 al.Sort();//排序一下動態數組
10 foreach (object cps in al)
11 Console.WriteLine(cps);//把這7個數用foreach循環打印出來