C# 70-536 – Chapter 4 Collections and Generics

  • 4957
  • 0
  • C#
  • 2011-05-11

C# 70-536 – Chapter 4

這章最主要是討論「集合(Collection)」的使用。在開發的時候最常用的就是集合了,所以非常重要…

 

ArrayList是一個簡單且無序的集合,可以儲存任何型別(Object),如果置放實質型別則會被Boxing

可以單獨加入一個項目,或是實作ICollection介面的物件。支援排序方法,也可以自訂排序方式,但需實做IComparer介面。

**若用foreach取用直接轉型時,轉型失敗會出現Exception

string[] strArray = { "Shelly", "Alonstar" };
ArrayList al = new ArrayList();
//al.Add(1); //Boxing

//新增
al.Add("1");
al.Insert(0, "3");
al.AddRange(strArray);
//插入
al.InsertRange(1, strArray);

al.Sort(new CaseInsensitiveComparer()); //CaseInsensitiveComparer表不分大小寫排序,繼承自IComparer.

foreach (string item in al) //利用foreach存取,可以直接轉型
{
	Console.WriteLine(item);
}

IEnumerator enumerator = al.GetEnumerator(); //利用列舉子存取,所有的泛型也都可以~
while (enumerator.MoveNext())
{
	Console.WriteLine(enumerator.Current);
}

佇列集合有二種:Queue (先進先出FIFO) & Stack (後進先出LIFO)

//FIFO Queue
Queue q = new Queue();
q.Enqueue("First");
q.Enqueue("Second");
q.Enqueue("Third");
Console.WriteLine("Queue has {0} numbers", q.Count);
while (q.Count > 0)
{
	Console.WriteLine("q.Dequeue : " + q.Dequeue());
}
Console.WriteLine("Queue has {0} numbers", q.Count);

/* Output
 * Queue has 3 numbers
 * q.Dequeue : First
 * q.Dequeue : Second
 * q.Dequeue : Third
 * Queue has 0 numbers
 */

//LIFO Stack
Stack t = new Stack();
t.Push("First");
t.Push("Second");
t.Push("Third");
Console.WriteLine("Stack has {0} numbers", t.Count);
while (t.Count > 0)
{
	Console.WriteLine("t.pop :" + t.Pop());
}
Console.WriteLine("Stack has {0} numbers", t.Count);

/* Output
 * Stack has 3 numbers
 * t.pop :Third
 * t.pop :Second
 * t.pop :First
 * Stack has 0 numbers			
 */

字典(Dictionary):是用來儲存Key / Value (DictionaryEntry)對應的集合。

System.Collections

最基本的型別是Hashtable,只能夠用key值來存取,無法用index,且以key的HashCode來判斷是否為使用同一key值,並進行Equal比較是否一致。如果key重覆會直接覆寫。

如果使用物件當key,每個物件在建立的時候都會有不同的HashCode,如果要讓Hashtable判斷相同,則需覆寫GetHashCode 及 Equal方法。或是實做IEqualityComparer,來指定比較的方式。

//Hashtable只可以用key值來存取

Hashtable hs = new Hashtable();
hs["First"] = "first";
hs["First"] = "sencod";
Console.WriteLine("First".GetHashCode());
Console.WriteLine("First".GetHashCode());
Console.WriteLine(hs["First"]);

/* Output
 * -1920739956
 * -1920739956
 * sencod        hs["First"]被覆寫,因為"First"的雜湊碼(GetHashCode)相同
 */

hs.Clear();
Publisher p1 = new Publisher();
Publisher p2 = new Publisher();
hs[p1] = "third";
hs[p2] = "forth";
Console.WriteLine(p1.GetHashCode());
Console.WriteLine(p2.GetHashCode());
Console.WriteLine(hs.Count);

/* Output
 * 45653674
 * 41149443
 * 2
 */

//HashTable 以hashcode為key,用object.Equals方法判斷是否相同
//如果要讓hasttable認為是相同的,要同時覆寫GetHasCode & Equals方法

hs.Clear();
Fish f1 = new Fish("Herring");
Fish f2 = new Fish("Herring");
hs[f1] = "Hello";
hs[f2] = "Hello";
Console.WriteLine(f1.GetHashCode());
Console.WriteLine(f2.GetHashCode());
Console.WriteLine(hs.Count);

/* Output
 * -153736088
 * -153736088
 * 1
 */

//指定key的比較方式, 必需實做IEqualityComparer
Hashtable hs2 = new Hashtable(new InsensitiveComparer());
hs2["One"] = "Hello";
hs2["one"] = "Hello";
Console.WriteLine("One hashcode = {0}, one hash = {1}", "One".GetHashCode(), "one".GetHashCode());
Console.WriteLine("hs2 has {0} members", hs2.Count);

/* Output
 * One hashcode = 1700053362, one hash = 1700053330
 * hs2 has 1 members
 */

類別Fish,覆寫GetHashCode & Equal

class Fish
{
	private string _name;

	public string Name
	{
		get { return _name; }
		set { _name = value; }
	}

	public Fish(string name)
	{
		_name = name;
	}



	public override int GetHashCode()
	{
		return _name.GetHashCode();
	}

	public override bool Equals(object obj)
	{
		Fish f = obj as Fish;
		if (f == null) return false;
		return f.Name == _name;
	}
}

實作IEqualityComparer

/// <summary>
/// HashTable 實做IEqualityComparer類別,來比較key是否相同
/// </summary>
public class InsensitiveComparer : IEqualityComparer
{
	CaseInsensitiveComparer _comparer = new CaseInsensitiveComparer();

	#region IEqualityComparer 成員

	bool IEqualityComparer.Equals(object x, object y)
	{
		if (_comparer.Compare(x, y) == 0)
			return true;
		else
			return false;
	}
	int IEqualityComparer.GetHashCode(object obj)
	{
		return obj.ToString().ToLowerInvariant().GetHashCode();
	}
	#endregion
}

SortedList 也是一個字典類別,但不同的是可以利用index存取,在加入項目的時候會自動排序。可以自訂排序方法,但需實作IComparer介面。

**另外CollectionsUtil提供了二個方法可以建立不分大小寫的Hashtable & SortedList,.Net也有內建幾個已經實做IComparer or IEqualityComparer的類別可以使用,例:StringComparer, CaseInsensitiveComparer

//SortedList 可用index & key 存取, 加入時直接排序
//自訂排序方式可實做Icomparer
SortedList sd = new SortedList();
sd.Add(3, "Hello1");
sd.Add(2, "Hello2");
sd.Add(1, "Hello3");

for (int i = 0; i < sd.Count; i++)
{
	Console.WriteLine("for i={0} : {1}", i, sd.GetByIndex(i));
}
foreach (DictionaryEntry entry in sd)
{
	Console.WriteLine("foreach entry={0} : {1}", entry.Key, entry.Value);
}

/* Output
 * for i=0 : Hello3
 * for i=1 : Hello2
 * for i=2 : Hello1
 * foreach entry=1 : Hello3
 * foreach entry=2 : Hello2
 * foreach entry=3 : Hello1
 */

但如果你需要使用index & key存取,但又不希望他自動排序,可以使用OrderedDictionary

OrderedDictionary od = new OrderedDictionary();
od.Add(3, "Hello1");
od.Add(2, "Hello2");
od.Add(1, "Hello3");

for (int i = 0; i < od.Count; i++)
{
	Console.WriteLine("for i={0} : {1}", i, od[i]);
}
/* Output
 * for i=0 : Hello1
 * for i=1 : Hello2
 * for i=2 : Hello3 
 */


//不分大小寫的Hashtable & SortedList
Hashtable inTable = CollectionsUtil.CreateCaseInsensitiveHashtable();
SortedList inList = CollectionsUtil.CreateCaseInsensitiveSortedList();

//不受文化特性影響區分大小寫
Hashtable hash = new Hashtable(StringComparer.InvariantCulture);
SortedList list = new SortedList(StringComparer.InvariantCulture);

位元集合

BitArray是一個用來儲存Boolean值,且可以調整大小的集合(調整Length屬性)。還可以直接使用邏輯運算。

BitVector32和BitArray有點不同,他的大小是固定32bit,但是可以直接調整每一個bit的值,可以用Data屬性直接回傳值。

//BitArray 只存boolean & 可調整長度
BitArray bitArray = new BitArray(3);
bitArray[0] = true;
bitArray[1] = false;
bitArray[2] = true;
bitArray.Length = 4; //增加長度
bitArray[3] = true;

BitArray bits1 = new BitArray(3);
bits1[1] = true;

for (int i = 0; i < bits1.Length; i++)
{
	Console.WriteLine("b1[{1}]:{0}", bits1[i], i);
}

BitArray b2 = new BitArray(3);
b2[0] = true;

for (int i = 0; i < b2.Length; i++)
{
	Console.WriteLine("b2[{1}]:{0}", b2[i], i);
}
//執行運算會修當改前的陣列
BitArray xorBits = bits1.Xor(b2);
for (int i = 0; i < xorBits.Length; i++)
{
	Console.WriteLine("b1[{3}]:{0}, b2[{3}]:{1}, b1[{3}] xor b2[{3}] = {2}", bits1[i], b2[i], xorBits[i], i);
}

/*
 * b1[0]:False
 * b1[1]:True
 * b1[2]:False
 * b2[0]:True
 * b2[1]:False
 * b2[2]:False
 * 做xor運算後.....
 * b1[0]:True, b2[0]:True, b1[0] xor b2[0] = True
 * b1[1]:True, b2[1]:False, b1[1] xor b2[1] = True
 * b1[2]:False, b2[2]:False, b1[2] xor b2[2] = False
 */

//BitVector32 可用來操作一個32-bit整數裡的位元,只能固定在32bit
//包裝單一位元
BitVector32 bitV = new BitVector32(0);
int firstBit = BitVector32.CreateMask();
int secondBit = BitVector32.CreateMask(firstBit);
int thirdBit = BitVector32.CreateMask(secondBit);

Console.WriteLine("firstBit:" + firstBit);
Console.WriteLine("secondBit:" + secondBit);
Console.WriteLine("thirdBit:" + thirdBit);
Console.WriteLine("before bitV.Data:" + bitV.Data);
Console.WriteLine(bitV);

bitV[firstBit] = true;
bitV[secondBit] = true;
bitV[thirdBit] = true;

Console.WriteLine("after bitV.Data:" + bitV.Data);
Console.WriteLine(bitV);

//利用CreateSection來包裝數值(書上的範例答案有誤)
BitVector32.Section firstSection = BitVector32.CreateSection(10);
BitVector32.Section secondSection = BitVector32.CreateSection(50, firstSection);
BitVector32.Section thirdSection = BitVector32.CreateSection(500, secondSection);

BitVector32 bit32 = new BitVector32(0);
bit32[firstSection] = 10;
bit32[secondSection] = 1;
bit32[thirdSection] = 192;

Console.WriteLine("firstsection:" + bit32[firstSection]);
Console.WriteLine("secondSection:" + bit32[secondSection]);
Console.WriteLine("thirdBit:" + bit32[thirdSection]);
Console.WriteLine("bit32.Data:" + bit32.Data);
Console.WriteLine(bit32);

/* Output
 * firstsection:10
 * secondSection:1
 * thirdBit:192
 * bit32.Data:196634
 * BitVector32{00000000000000110000000000011010}
 */

特別的字串集合:StringCollection & StringDictioyary & NameValueCollection

StringCollection & StringDictioyary 用法和ArrayList是類似的,但是key和value只能使用字串。最裡面的Data還是使用ArrayList包裝,所以還是有使用到Boxing…

但NameValueCollection有個比較特別的地方:當key值相同時,value他會自動組合成字串陣列。

//如同 ArrayList ,但只能放字串
StringCollection sc = new StringCollection();
sc.Add("First");
sc.Add("Second");

//key和值都只能是字串
StringDictionary sd = new StringDictionary();
sd.Add("First", "Hello1");
sd.Add("Second", "Hello2");
//sd.Add("first", "Hello3"); key不分大小寫,重覆直接exception

//key 不分大小寫,重覆的會變陣列
NameValueCollection nvc = new NameValueCollection();
nvc.Add("First", "Hello1");
nvc.Add("first", "Hello2");
nvc.Add("Second", "Hello3");
nvc.Add("First", "Hello4");

for (int i = 0; i < nvc.Count; i++)
{
	Console.WriteLine("key:{0} values:{1}", nvc.Keys[i], nvc[i]);
}

/* Output
 * key:First  values:Hello1,Hello2,Hello4
 * key:Second values:Hello3
 */

在.NET Framework2.0中,為了能更為便利的使用物件,減少Boxing問題,在集合中也增加了泛型方法。以下是各種集合對應到的泛型集合方法。使用的方式一樣,只是在宣告的時候必需特別指定使用的型別,且不能改變。而在Dictionary的DictionaryEntry則改用了KeyValuePair新泛型型別。而比較運算則需實作泛型的IComparer<T>及IEqualityComparer<T>

**開發2.0以上版本,最好都使用泛型集合較為安全

System.Collection.Gereric

使用Dictionary<TKey, TValue>泛型集合:

Dictionary<int, string> dict = new Dictionary<int, string>();
dict.Add(3, "Three");
dict.Add(2, "Second");
dict.Add(1, "One");

//利用KeyValuePair存取
foreach (KeyValuePair<int, string> item in dict)
{
	Console.WriteLine("key:{0}, value:{1}", item.Key, item.Value);
}

 

 

另外還新增了一個比較特別的佇列集合:LinkedList,可以使用相鄰的方式來存取(Next, Pervious),而單一項目則以LinkedListNode類別來存取。

 

LinkedList<string> links = new LinkedList<string>();
LinkedListNode<string> root =  links.AddFirst("Root");
LinkedListNode<string> home1 = links.AddAfter(root, "Home1");
LinkedListNode<string> home11 = links.AddAfter(home1, "Home11");

Console.WriteLine(root.Value);
Console.WriteLine(root.Next.Value);
Console.WriteLine(root.Next.Next.Value);
Console.WriteLine(root.List.First.Value);
Console.WriteLine(root.List.Last.Value);

/*
 * root.Value = Root
 * root.Next.Value = Home1
 * root.Next.Next.Value = Home11
 * root.List.First.Value = Root
 * root.List.Last.Value = Home11
 */

 

 

Dotblogs 的標籤: