集合物件的特性:允許資料項目的增減,可以巡覽集合中的資料項目。
集合的種類
清單、佇列、位元陣列、雜湊表和字典等等都是.NET底下的一種集合,所有集合類別都由以下二個命名空間提供:
1. System.Collections 命名空間所提供的類別
| 類別 | 說明 |
|---|---|
| ArrayList | 使用大小會視需要動態增加的陣列,實作 IList 介面。 |
| CaseInsensitiveComparer | 比較兩個物件是否相等,忽略字串的大小寫。 |
| CaseInsensitiveHashCodeProvider | 已過時。使用忽略字串大小寫的雜湊演算法,提供物件的雜湊程式碼。 |
| CollectionBase | 提供強式型別集合的 abstract 基底類別。 |
| ReadOnlyCollectionBase | 提供強式非泛型唯讀集合的 abstract 基底類別。 |
| Comparer | 比較兩個物件是否相等,其中字串比較是區分大小寫的。 |
| DictionaryBase | 提供索引鍵/值組配對強式集合的 abstract 基底類別。 |
| Stack | 表示簡單之物件的後進先出 (Last-In First-Out,LIFO) 非泛型集合。 |
| Queue | 表示物件的先進先出 (FIFO) 集合。 |
| Hashtable | 表示根據索引鍵的雜湊程式碼,所整理的索引鍵/值組集合。 |
| SortedList | 表示索引鍵/值組配對的集合,這個集合按索引鍵排序,而且可以按索引鍵和索引存取。 |
| BitArray | 管理以布林 (Boolean) 表示的位元值之精簡陣列,其中 true 表示位元為開啟 (1),而 false 表示位元為關閉 (0)。 |
| StructuralComparisons | 提供物件,用以執行兩個集合物件的結構比較。 |
2. System.Collections.Specializeds 命名空間所提供的類別
| 類別 | 說明 |
|---|---|
| CollectionsUtil | 建立忽略字串大小寫的集合。 |
| HybridDictionary | 在集合很小時,使用 ListDictionary 來實作 IDictionary,然後在集合變大時,切換至 Hashtable。 |
| ListDictionary | 使用單向連結串列來實作 IDictionary 。建議用於通常包含 10 個或更少項目的集合。 |
| NameObjectCollectionBase | 提供 abstract 基底類別給可以用索引鍵或索引來存取之關聯 String 索引鍵和 Object 值的集合。 |
| NameObjectCollectionBase.KeysCollection | 表示集合索引鍵 String 的集合。 |
| NameValueCollection | 表示相關 String 索引鍵和 String 值的集合,而這些可以利用索引鍵或索引來存取。 |
| OrderedDictionary | 表示根據索引鍵/索引排序的索引鍵/值組集合。 |
| StringCollection | 表示字串的集合。 |
| StringDictionary | 實作雜湊資料表,其中的索引鍵與資料值均採用強式型別宣告為字串,而不是物件。 |
| StringEnumerator | 支援在 StringCollection 上的簡易反覆運算。 |
ArrayList 類別
ArrayList 是一種基本的集合物件,有著以下特性:
- 隨著元素逐漸加入,會視需要透過重新配置的方式自動增加容量。
- 在這個集合的項目是沒有經過排序的,可以儲存任何型別(Object),也接受 null 值,也允許重複的項目。
- 如果置放實質型別,則會被 Boxing。
ArrayList 方法
- Add :加入一個項目至最後頭
- AddRange :加入一個陣列或集合
- Insert :插入一個陣列
- InsertRange :插入一個陣列或集合
- Remove :移除特定物件之第一個符合的元素。
- RemoveAt :移除第 n 個項目
- RemoveRange :移除第 n~m 個項目
- Clear :清除所有項目
- GetEnumerator :傳回會逐一查看 ArrayList 的列舉程式。
- IndexOf :傳回 ArrayList 或其中一部分中值的第一個項目之以零起始的索引。
- LastIndexOf :
- Contains :判斷某元素是否在 ArrayList 中。
- BinarySearch :使用二進位搜尋演算法來尋找在排序的 ArrayList 中或在其中一部分中的特定元素。
- Reverse :反轉 ArrayList 或其中一部分中元素的順序。
- Sort :排序 ArrayList 或其中一部分的項目。
- Synchronized :傳回同步 (安全執行緒) 的清單包裝函式。
- ToArray :將 ArrayList 的元素複製到新的陣列。
- CopyTo :複製至一維陣列。
底下內容,皆以 ArrayList 來說明集合的基本操作功能。
加入或移除資料項目
ArrayList alCol = new ArrayList();
// 集合項目可以是 value type 或 reference type
// 加入一個項目
string s = "Hello";
alCol.Add(s);
alCol.Add("hi");
alCol.Add(50);
alCol.Add(new object());
// 加入一個陣列
string[] anArray = new string[] { "more", "or", "less" };
alCol.AddRange(anArray);
// 加入一個集合
object[] anotherArray = new object[] { new object(), new ArrayList() };
alCol.AddRange(anotherArray);
// 插入一個項目
alCol.Insert(3, "Hey All");
// 插入一個陣列
string[] moreStrings = new string[] { "goodnight", "see ya" };
alCol.InsertRange(4, moreStrings);
// 更新一個項目
alCol[3] = "Hi all";
// 移除一個項目
alCol.Add("Hello");
alCol.Remove("Hello");
// 移除第1個項目
alCol.RemoveAt(0);
// 移除前4個項目
alCol.RemoveRange(0, 4);
//判斷是否存在某個項目
if (alCol.Contains("Hello"))
{
//找出某個item的Index
int index2 = alCol.IndexOf("Helll"); //non match retrun -1
int index = alCol.IndexOf("Hello");
alCol.RemoveAt(index);
}
//清除所有項目
alCol.Clear();
巡訪資料項目(Iterate)
要取得 ArrayList 中的項目,有以下幾種方法:
- 1. 利用 indexer
for (int x = 0; x < coll.Count; ++x) { Console.WriteLine(coll[x]); } - 2. 利用 foreach : 只要支援 IEnumerable 介面的物件,就可以使用 foreach 語法,簡單地達到巡訪所有 item 的操作
foreach (object item in coll) { Console.WriteLine(item); } //若已知集合中的項目皆為同一型別,可以在foreach指定型別,以節省物件轉型的時間 foreach (string item in coll) { Console.WriteLine(item); } - 3. 利用 IEnumerator : ArrayList 支援 IEnumerable 介面,它允許使用列舉值 (IEnumerator) 來存取串列。
IEnumerator enumerator = coll.GetEnumerator(); //透過 GetEnumerator 方法,取得 IEnumerator while (enumerator.MoveNext()) //列舉值一開始會位於集合中的第一個元素之前 { Console.WriteLine(enumerator.Current); //Current:取得目前項目 } enumerator.Reset(); //利用Reset, 將 enumerator 重新指向第一個
備註
列舉集合中的元素時,並不會獨佔集合的存取權,因此在集合中逐一列舉,本質上不是執行緒安全的程序。 若要確保列舉期間的執行緒安全,必須在整個列舉期間鎖定該集合,也就是必須實作自己的同步處理機制。
集合介面的一致性
在處理集合項目的操作時, .Net 定義了幾組介面,以確保實作時可以呼叫的共通方法。 例如:上面提到的 IEnumerable,該介面提供了巡訪資料的共通方法。
.Net 定義了幾組介面,以確保實作時可以呼叫的共通方法
- IEnumerable :提供了巡訪資料的共通方法
- IEnumerator :提供了巡訪資料的共通方法
- ICollection :確保每個集合都支援取得集合項目的共通方法,以及將集合複製到一個Array物件的共通方法。
- IList :表示可以個別由索引存取之物件的非泛型集合。
- IComparer :公開比較兩個物件的方法。
//IEnumerable 介面的組成成員
public interface IEnumerable
{
IEnumerator GetEnumerator();
}//IEnumerator 介面的組成成員
public interface IEnumerator
{
object Current { get; }
bool MoveNext();
void Reset();
}//ICollection 介面的組成成員
public interface ICollection
{
int Count { get; } //取得在 ICollection 中所包含的元素數。
bool IsSynchronized { get; } //取得值,指出對 ICollection 的存取是否為同步 (安全執行緒)。
void CopyTo(Array array,int index) //從特定的 Array 索引開始,複製 ICollection 項目至 Array。
IEnumerator GetEnumerator() //傳回會逐一查看集合的列舉程式。 (繼承自 IEnumerable)。
}//IList 介面的組成成員
public interface IList
{
Object this[int index { get; set; }
bool IsFixedSize { get; }
bool IsReadOnly { get; }
int Add (Object value)
void Clear ()
bool Contains (Object value)
int IndexOf (Object value)
void Insert (int index, Object value)
void Remove (Object value)
void RemoveAt (int index)
}//IComparer 介面的組成成員
public interface IComparer
{
int Compare (Object x,Object y)
}排序
ArrayList 類別支援對集合進行排序,只要呼叫它的 Sort 方法即可,例如:
coll.Sort(); //以預設方法排序 coll.Reverse(); //反向排序
ArrayList.Sort 共有三個覆載方法。
public virtual void Sort(); public virtual void Sort(IComparer comparer); public virtual void Sort(int index, int count, IComparer comparer);
不帶參數的 Sort 會依照集合元素本身的比較原則進行排序,也就是集合元素必須實作 IComparable 介面才可以使用這個排序方法。
帶 IComparer 型別參數的 Sort ,就不會根據 item 本身的比較原則,而是依據這個參數當做比較原則。
使用比較子進行比較
若要支援 IComparer 介面,則必須實作 Compare 方法:
int Compare(object x, object y);
Compare 是用來比較兩個物件的大小。其回傳值意義如下:
- 負值: x 小於 y。
- 零值: x 等於 y。
- 正值: x 大於 y。
範例
//定義一個比較子
public class DescendingComparer : IComparer
{
CaseInsensitiveComparer _comparer = new CaseInsensitiveComparer();
public int Compare(object x, object y)
{
//將二值對調,呼叫CaseInsensitiveComparer.Compare,以比較大小
return _comparer.Compare(y, x);
}
}
private void button3_Click(object sender, EventArgs e)
{
ArrayList coll = new ArrayList();
coll.Add("First");
coll.Add("Second");
coll.Add("Third");
coll.Add("Fourth");
coll.Sort(); //以預設方法排序
coll.Sort(new DescendingComparer()); //自訂 compare 方法排序
}
實作類別的 IComparable 介面,以支援比較
若要支援 IComparable 介面,則必須實作 CompareTo 方法。
int CompareTo(object obj);
CompareTo 是用來表示目前的執行個體應該排序在比較物件的前面、後面或相同位置。其回傳值意義如下:
- 負值: 執行個體排序在 obj 前面。
- 零值: 執行個體與 obj 排序在相同位置。
- 正值: 執行個體排序在 obj 後面。
範例
public class myAge: IComparable
{
private int _value;
public myAge(int age)
{
_value = age;
}
public int Value()
{
return _value;
}
public override string ToString()
{
return _value.ToString();
}
public int CompareTo(object obj)
{
if (obj is myAge)
{
myAge age = (myAge)obj;
return _value.CompareTo(age.Value());
}
else
throw new ArgumentException("object is not an Age");
}
}
private void button5_Click(object sender, EventArgs e)
{
ArrayList ages = new ArrayList();
ages.Add(new myAge(10));
ages.Add(new myAge(40));
ages.Add(new myAge(30));
ages.Add(new myAge(20));
foreach (myAge age in ages)
{
Console.WriteLine(age);
}
//10 40 30 20
ages.Sort();
foreach (myAge age in ages)
{
Console.WriteLine(age);
}
//10 20 30 40
}
搜尋
下面三種方法都可以用來搜尋 ArrayList 中是否存在某個項目:
- IndexOf :傳回 ArrayList 中第一個符合搜尋項目的索引。這個方法採用線性搜尋,是一種 O(n) 運算。
- Contains :判斷某元素是否在 ArrayList 中,此方法會呼叫 Object.Equals 來判斷是否相等。這個方法採用線性搜尋,是一種 O(n) 運算。
- BinarySearch :使用二進位搜尋演算法來尋找在排序的 ArrayList 中的元素。這個方法是 O(log n) 運算。
ArrayList myAL = new ArrayList();
for (int i = 0; i <= 4; i++) // 0 2 4 6 8
myAL.Add(i * 2);
int obj = 4;
// Using Contains
if (!myAL.Contains(obj))
Console.WriteLine("({0}) is not found", obj);
else
{
int index1 = myAL.IndexOf(obj);
Console.WriteLine("({0}) is at index {1}.", obj, index1);
}
// Using BinarySearch
int index2 = myAL.BinarySearch(obj);
if (index2 < 0)
Console.WriteLine("({0}) is not found. The next larger object is at index {1}.", obj, ~index2);
else
Console.WriteLine("The object to search for ({0}) is at index {1}.", obj, index2);
//(3) is not found
//(3) is not found. The next larger object is at index 2.
//(4) is at index 2.
//The object to search for (4) is at index 2.
備註
ArrayList.Sort() 這個方法使用 Array.Sort,並採用 QuickSort 演算法。QuickStort 演算法是比較排序 (也稱為不穩定的排序,亦即,如果有兩個項目相同,可能無法保留其順序。),表示「 小於或等於」比較作業會判斷最終排序清單中應該先發生兩個項目中的。不過,如果兩個項目相等,其原本的順序可能不會保留。相反地,穩定排序可以保留相等項目的順序。若要執行穩定排序,您必須實作自訂 IComparer 介面,以搭配使用這個方法的其他多載。
平均而言,這個方法是 O(n log n) 運算,其中 n 為 Count;最壞的狀況是變成 O(n ^ 2) 運算。
IsSynchronized 屬性和 ArrayList.Synchronized 方法
ArrayList 本身並不是一個執行緒安全的物件,若要在多執行緒作業中訪問,就必須自已動手調用 lock 機制以保持執行緒同步。例如:
ArrayList al = new ArrayList();
lock (al.SyncRoot)
{
al.Add("test1");
al.Add("test2");
//....
}
如果使用 ArrayList.Synchronized 方法取得的實體,那麼就不用考慮執行序同步的問題,因為這個實體本身就是執行序安全的。 實際上 ArrayList 內部有實作一個執行序安全的類別, Synchronized 回傳的就是這個類別,它裡面每個屬性都用了 lock 關鍵字來保護執行緒同步。
- IsSynchronized :指出對 ArrayList 的存取是否已同步處理 (執行緒安全)。
- Synchronized :傳回同步 (安全執行緒) 的清單包裝函式。
ArrayList al = new ArrayList();
ArrayList sync_al = ArrayList.Synchronized(al);
sync_al.Add("test1");
sync_al.Add("test2");
總結
- The .NET Framework supports a variety of collection classes that can be used in different circumstances.
- The ArrayList is a simple collection of unordered items.
- The Add and AddRange methods of the ArrayList are used to add items to an ArrayList .
- The Insert and InsertRange methods of the ArrayList are used to insert items into specific places in a collection.
- The Remove , RemoveAt , and RemoveRange methods of the ArrayList are used to delete items from a collection.
- The indexer of the ArrayList can be used to iterate over a collection.
- The IEnumerable and IEnumerator interfaces can be used to enumerate through a collection as well.
- The foreach construct in Visual Basic and C# use the IEnumerable interface to concisely iterate over a collection.

沒有留言:
張貼留言