例如下例中的聲明將創建一個整型數組:
private int [] _numbers = new int[100];我們可以通過索引來訪問數組中的每一項。
int j = _numbers[9];
除此之外,我們還可以使用foreach或者枚舉器來遍歷數組
你可以使用類似於一維數組聲明的標記來創建一個多維數組。
private int[,] _multi = new int[10, 10];
為了初始化這些元素,我們必須考慮到數組中的每一個維度。數組類有兩個主要的缺點,正是這兩個缺點使得.Net Framework中其它的集合類型有了用武之地。第一個缺點影響數組的大小調整:數組不能動態的調整大小。如果你需要調整數組某一維度的大小,你就必須重新創建一個數組並從原數組中將所有已存在的元素拷貝至新數組。調整大小非常耗時:一個新的數組必須被分配空間,已有數組中的全部元素必須被拷貝到新數組中。盡管這種在托管堆上的拷貝和移動的代價已經不像C或者C++時代那樣昂貴,但是依然會耗費時間。而更重要的是這種操作可能導致陳舊數據被應用。考慮下面的代碼片斷:
private string[] _citIEs = new string[100];
public void SetDataSource()
{
myListBox.DataSource = _citIEs;
}
public void AddCity(string cityName)
{
string[] temp = new string[_citIEs.Length + 1];
_citIEs.CopyTo(temp, 0);
temp[_citIEs.Length] = cityName;
_citIEs = temp;
}
即便是AddCity方法被調用之後,列表框所使用的數據源仍然是_citIEs數組的老版本拷貝。新添加的城市永遠不會顯示在列表框之中。
ArrayList類是構建在數組上的一種高層次抽象。ArrayList集合混合了一維數組和鏈表的特征。你可以在ArrayList中進行插入操作,也可以調整它的大小。ArrayList將其大部分職責都委托給其內部包含的數組,這意味著ArrayList類在功能特性上和Array類是非常相似的。當我們可以使用ArrayList來輕松的應對未知大小的集合,這也是ArrayList較Array而言的主要優點。ArrayList可以隨時增長或縮減。雖然我們仍然需要付出拷貝和移動數組元素的代價,但是這些算法的代碼是已經寫好並經過測試的。由於ArrayList對象內部儲存數據的數組是封裝好的,也不會出現陳舊數據的問題:客戶程序將指向ArrayList對象而不是內部數組。ArrayList集合是C++標准類庫中的vector類在.Net Framework中的版本。
隊列和棧類在System.Array基礎上提供了專門的接口。通過這些類的特定的接口實現了先進先出的隊列和後進先出的棧。我們要始終牢記這些集合是使用其內部的一維數組來儲存數據的。當我們改變它們的大小時同樣會受到性能上的損失。
.Net中不包含鏈表結構的集合。由於有高效的垃圾收集機制,表結構出場亮相的次數也減少了。如果你的確需要實現鏈表行為時,你有兩種選擇。如果你引起經常要添加或移除項目而使用列表時,你可以使用字典類簡單的儲存鍵,對於值則賦以null。當需要實現一個鍵/值的單鏈表時,你可以使用ListDictionary類。或者你可以使用HyBridDictionary類。當集合較小時,HyBridDictionary類會使用ListDictionary來應對,而對於較大的集合則選用HashTable。這幾個集合和其它許多集合一起位於System.Collections.Specialized命名空間下。盡管如此,如果你為了實現某些用戶指令的目的而使用鏈表結構的話,那麼你完全可以使用ArrayList集合來代替它。盡管ArrayList內部是使用數組來進行存儲的,但是它也可以完成在任意位置插入元素的功能。
另外兩種支持基於字典的集合是SortedList和Hashtable。它們都包含鍵/值對。SortedList會對鍵進行排序而Hashtable不會。Hashtable提供了對給定鍵的快速搜索,而SortedList提供了按鍵的順序遍歷元素的功能。Hashtable通過做為鍵的對象的哈希值來進行搜索,如果哈希鍵是足夠高效的話,那麼其每次搜索操作所耗費的時間是一個常數,即時間復雜度為0(1)。SortedList使用二分法來進行搜索,這種算法操作的時間復雜度為0(ln n)。
最後我們來介紹一下BitArray類。顧名思義,這個類是用來存儲二進制數據的。BitArray類使用一個整型的數組來儲存數據。整型數組中的每個存儲單元儲存32個二進制值。這樣做可以達到壓縮的目的,但是同樣也會降低性能。每次對BitArray進行get或者set操作都會引發對儲存著目標數據和其它31個二進制數據的整數的操作。BitArray包含了一些方法來對其內部的值進行布爾型操作,例如:OR,XOR,AND和NOT。這些方法使用BitArray做為參數,可以被用來快速過濾BitArray中的多位二進制數。BitArray針對位操作做了專門的優化,應當使用它來存儲那些經常進行做為掩碼的二進制標記集合,而不應當使用一般的布爾型的數組來代替。
除了Array類之外,在.Net Framework 1.x版本的C#中再也沒有其它集合類是強類型的。它們儲存的都是Object的引用。C#泛型中包含了一種新版本的拓撲結構,它能夠以一種更加普遍化的方式被創建。泛型是創建類型安全集合的最好方法。你也可以通過現在的System.Collection命名空間中包含的抽象基類在非類型安全的集合上構建你自己的類型安全接口:CollectionBase和ReadOnlyCollectionBase提供了存儲鍵/值集合的基類。DictionaryBase類使用的是哈希表的實現方法,他的功能特點和哈希表非常相似。
當你的類包含集合時,你會希望為將它暴露給你的類用戶。你有兩種方法來達到這個目的:使用索引器或者實現IEnumerable接口。在本節開始的部分,我向你展示了數組如何通過[]標記來獲取其中的項目,你也可以使用foreach來遍歷數組中的項目。
你可以為你的類創建多維索引器。這很類似於C++中重載操作符[]一樣。就像C#中的數組一樣,你可以創建多維的索引器:
public int this[int x, int y]
{
get
{
return ComputeValue(x, y);
}
}
添加索引功能通常意味著你的類型內部包含一個集合。而這也意味這你的類型應當支持IEnumerable接口。IEnumerable接口提供了一種標准的迭代遍歷集合中所有元素的機制。
public interface IEnumerable
{
IEnumerator GetEnumerator();
}
GetEnumerator方法返回一個實現了IEnumerator接口的對象。IEnumerator接口支持對集合的遍歷:
public interface IEnumerator
{
object Current { get;}
bool MoveNext();
void Reset();
}
除了IEnumerable接口外,如果你的類型要模擬一個數組,那麼你還應當考慮IList和ICollection接口。如果你的類型要模擬一個字典,那麼你應當考慮實現IDictionary接口。當然,你可以自己來實現這些龐大的接口,如果要解釋實現方法的話,我恐怕需要多花上許多篇幅。其實有一個更簡單的解決辦法:當我們要創建特殊目的的集合時,我們可以從CollectionBase或者DictionaryBase來派生出我們的類。
讓我們來回顧一下本節所覆蓋的內容。一個最好的集合取決於它要執行的操作和應用程序對空間和時間的要求。在大多數情況下,Array類提供了最高效的集合容器。C#中多維數組的出現意味著我們可以非常簡單的模擬多維結構而不必擔心犧牲性能。當你的程序需要更加靈活的添加和刪除項時,你可以哪些使用更加靈活的集合類型。最後,當你要創建一個模擬集合的類時,應當為其實現索引器和IEnumerable接口。
for (int i = 0; i < _theBoard.GetLength(0); i++)
{
for (int j = 0; j < _theBoard.GetLength(1); j++)
{
_theBoard[i, j] = new Square();
}
}
但是在多維數組中,你擁有更加靈活的遍歷元素方式。我們可以通過數組的索引來獲取其中合法的元素:
Square sq = _theBoard[4, 4];
如果你需要遍歷整個集合,你可以使用迭代器
foreach(Square sq in _theBoard)
{
sq.PaintSquare();
}