C#中的集合类比较和说明,组织数据在内存中的存储和取用
2008-06-28 10:35
441 查看
System.Collections命名空间
System.Collections命名空间包含接口和类,这些接口和类定义各种对象(如,列表、队列、位数组、哈希表和字典)的集合。它们主要有三种类型:第一组类型是一组接口,用来定义集合必须遵守的一系列协定。第二组类型包括一组常用集合类型。例如:ArrayList、Hashtable、Stack、Queue。我们可以非常方便地使用这些来检索和存储内存中的数据。第三组类型用来支持类型严格的集合的创建。通过CollectionBase类提供数据存储,我们可以方便地创建自定义集合而且是类型严格的——这一点很重要。
下面我们逐一深入探讨上面提到的三组类型。首先,我们要全面了解一下命名空间里面的接口,看一看这些接口能够为我们提供哪些协定。然后讨论如何使用常用的集合类型。最后讨论利用CollectionBase类创建自定义的集合类型。
集合接口
System.Collections.ICollection接口
该接口定义了所有集合的大小、枚举数和同步方法。
System.Collections.IEnumerable
System.Collections.ICollection
该接口有如下成员:
System.Collections.IComparer接口
该接口公开了比较两个对象的方法。默认实现为Comparer类。该接口只有一个成员,就是Compare方法。该方法比较两个对象并返回一个值,指示一个对象是小于、等于还是大于另一个对象。如果比较两个对象X和Y。
System.Collections.IDictionary接口
该接口表示键/值对的集合。具有以下属性和方法:
System.Collections.IEnumerable接口
该接口公开枚举数,该枚举数支持在集合上进行简单迭代。方法GetEnumerator是该接口唯一的公共方法。返回可循环访问集合的枚举数。
System.Collections.IEnumerator接口
该接口支持在集合上进行简单迭代。其属性Current用于获取集合中的当前元素。方法MoveNext()方法将枚举数推进到下一个元素。方法Reset()将枚举数设置为其初始位置,该位置位于集合中第一个元素之前。
System.Collections.IhashCodeProvider接口
使用自定义哈希函数为对象提供哈希代码。方法GetHashCode()返回指定对象的哈希代码。
System.Collections.Ilist接口
该接口表示可按照索引单独访问的一组对象。其属性包括:
该接口的方法包括:
以上就是System.Collections命名空间的接口。定义新集合和使用这些集合都必须遵守这些接口定义的协定。
常用集合类型
常用集合类型主要包括:ArrayList、Hashtable、Stack、Queue类。
System.Collections.ArrayList类
该类实现了IList接口。该类的容量,也即该列表所能包含的元素数,其大小可按需动态增加。可通过调用TrimToSize()或Capacity属性减少容量。该类的元素索引从0开始,即列表中的头一个元素的索引是0。该类允许重复元素并且接受空引用。示例:
ArryList类包含的主要属性有:
怎么样?!够眼晕的吧。看到这里不难发现同步问题是使用好集合类的关键。通过集合进行枚举,其实不是一个线程安全的过程。当对集合进行同步处理的时候,其它线程仍然可能访问集合,这样可能会导致枚举数引发异常。要彻底在枚举过程中保证线程安全,最简单的方法就是在枚举过程中锁定集合。如下例:
[align=left]classClass1[/align]
[align=left]{[/align]
[align=left][STAThread][/align]
[align=left]staticvoidMain(string[]args)[/align]
[align=left]{[/align]
[align=left]ArrayListal=newArrayList();[/align]
[align=left]al.Add("aaa");[/align]
[align=left]al.Add("bbb");[/align]
[align=left]al.Add("ccc");[/align]
[align=left]al.Add("ddd");[/align]
[align=left]lock(al.SyncRoot)[/align]
[align=left]{[/align]
[align=left]foreach(objectiteminal)[/align]
[align=left]{[/align]
[align=left]System.Console.WriteLine(item);[/align]
[align=left]}[/align]
[align=left]}[/align]
[align=left][/align]
[align=left]System.Console.Read();[/align]
}
}
但是仅仅在枚举过程中锁定ArrayList还是不够的,如果希望线程安全地使用ArrayList就必须使用ArrayList.Synchronized方法所返回的包装来完成所有操作。如下例:
[align=left]usingSystem;[/align]
[align=left]usingSystem.Collections;[/align]
[align=left][/align]
[align=left]namespaceConsoleApplication1[/align]
[align=left]{[/align]
[align=left]///<summary>[/align]
[align=left]///Class1的摘要说明。[/align]
[align=left]///</summary>[/align]
[align=left]classClass1[/align]
[align=left]{[/align]
[align=left][STAThread][/align]
[align=left]staticvoidMain(string[]args)[/align]
[align=left]{[/align]
[align=left]ArrayListal=newArrayList();//创建一个ArrayList[/align]
[align=left]ArrayListalsync=ArrayList.Synchronized(al);//为al创建一个线程安全的包装[/align]
[align=left]if(al.IsSynchronized==true)[/align]
[align=left]{[/align]
[align=left]System.Console.WriteLine("al是线程安全的。");[/align]
[align=left]}[/align]
[align=left]else[/align]
[align=left]{[/align]
[align=left]System.Console.WriteLine("al不是线程安全的。");[/align]
[align=left]}[/align]
[align=left]if(alsync.IsSynchronized==true)[/align]
[align=left]{[/align]
[align=left]System.Console.WriteLine("alsync是线程安全的。");[/align]
[align=left]}[/align]
[align=left]else[/align]
[align=left]{[/align]
[align=left]System.Console.WriteLine("alsync不是线程安全的。");[/align]
[align=left]}[/align]
[align=left]//添加列表元素[/align]
[align=left]alsync.Add("aaa");[/align]
[align=left]alsync.Add("bbb");[/align]
[align=left]alsync.Add("ccc");[/align]
[align=left]alsync.Add("ddd");[/align]
[align=left]alsync.Add("eee");[/align]
[align=left]//锁定[/align]
[align=left]lock(alsync.SyncRoot)[/align]
[align=left]{[/align]
[align=left]//枚举[/align]
[align=left]foreach(objectiteminal)[/align]
[align=left]{[/align]
[align=left]System.Console.WriteLine(item);[/align]
[align=left]}[/align]
[align=left]System.Console.WriteLine();[/align]
[align=left]}[/align]
[align=left]//枚举[/align]
[align=left]IEnumeratori=alsync.GetEnumerator();[/align]
[align=left]while(i.MoveNext())[/align]
[align=left]{[/align]
[align=left]System.Console.WriteLine(i.Current);[/align]
[align=left]}[/align]
[align=left][/align]
[align=left]System.Console.Read();[/align]
[align=left]}[/align]
[align=left]}[/align]
[align=left]}[/align]
[align=left][/align]
[align=left]除了属性之外ArrayList还有一大堆方法需要了解:[/align]
[align=left][/align]
这些方法看上去都不复杂。不过我们还是一起来研究几个有意思或者常用的方法。首先来看一下这个Add方法吧。该方法可以在ArrayList的结尾处添加一个新的对象。唯一的参数Value用来接受要加到ArrayList末尾对象。ArrayList接受空引用(VisualBasic中为Nothing)作为有效值并且允许重复的元素。
[align=left]try[/align]
[align=left]{[/align]
[align=left]//添加列表元素[/align]
[align=left]//唯一的参数Value用来接受要加到ArrayList末尾对象。[/align]
[align=left]alsync.Add("aaa");[/align]
[align=left]alsync.Add("bbb");[/align]
[align=left]alsync.Add("ccc");[/align]
[align=left]//ArrayList允许接受重复元素[/align]
[align=left]alsync.Add("ddd");[/align]
[align=left]alsync.Add("ddd");[/align]
[align=left]alsync.Add("eee");[/align]
[align=left]alsync.Add("ffff");[/align]
[align=left]//ArrayList接受空引用[/align]
[align=left]alsync.Add(null);[/align]
[align=left]}[/align]
[align=left]catch(System.NotSupportedExceptionexp)[/align]
[align=left]{[/align]
[align=left]//ArrayList为只读,或者有固定大小将会引发此异常[/align]
[align=left]System.Console.WriteLine(exp.Message);[/align]
[align=left]}[/align]
[align=left][/align]
该方法可能引发System.NotSupportedException异常。如果ArrayList为只读,或者有固定大小将会引发此异常。而AddRange方法与Add方法有所不同。该方法把ICollection的元素添加到ArrayList的末尾。唯一的参数c是ICollection接口类型,其元素会被添加到ArrayList的末尾。该参数如果为空引用则引发ArgumentNullException异常。如果ArrayList为只读或者具有固定大小则引发NotSupportedException异常。下例把一个Queue添加到ArrayList的末尾。
[align=left]staticvoidMain(string[]args)[/align]
[align=left]{[/align]
[align=left]ArrayListal=newArrayList();//创建一个ArrayList[/align]
[align=left]ArrayListalsync=ArrayList.Synchronized(al);//为al创建一个线程安全的包装[/align]
[align=left][/align]
[align=left]try[/align]
[align=left]{[/align]
[align=left]//添加列表元素[/align]
[align=left]//唯一的参数Value用来接受要加到ArrayList末尾对象。[/align]
[align=left]alsync.Add("aaa");[/align]
[align=left]alsync.Add("bbb");[/align]
[align=left]alsync.Add("ccc");[/align]
[align=left]//ArrayList允许接受重复元素[/align]
[align=left]alsync.Add("ddd");[/align]
[align=left]alsync.Add("ddd");[/align]
[align=left]alsync.Add("eee");[/align]
[align=left]alsync.Add("ffff");[/align]
[align=left]//ArrayList接受空引用[/align]
[align=left]alsync.Add(null);[/align]
[align=left]}[/align]
[align=left]catch(System.NotSupportedExceptionexp)[/align]
[align=left]{[/align]
[align=left]//ArrayList为只读,或者有固定大小将会引发此异常[/align]
[align=left]System.Console.WriteLine(exp.Message);[/align]
[align=left]}[/align]
[align=left]System.Console.WriteLine("没有把队列加到ArrayList之前!");[/align]
[align=left]IEnumeratori=alsync.GetEnumerator();[/align]
[align=left]while(i.MoveNext())[/align]
[align=left]{[/align]
[align=left]System.Console.WriteLine(i.Current);[/align]
[align=left]}[/align]
[align=left][/align]
[align=left]//创建并初始化一个队列[/align]
[align=left]QueuemyQueue=newQueue();[/align]
[align=left]myQueue.Enqueue("jumped");[/align]
[align=left]myQueue.Enqueue("over");[/align]
[align=left]myQueue.Enqueue("the");[/align]
[align=left]myQueue.Enqueue("lazy");[/align]
[align=left]myQueue.Enqueue("dog");[/align]
[align=left][/align]
[align=left]//把队列添加到ArrayList的末尾[/align]
[align=left]try[/align]
[align=left]{[/align]
[align=left]alsync.AddRange(myQueue);[/align]
[align=left]}[/align]
[align=left]catch(System.ArgumentNullExceptionexp)[/align]
[align=left]{[/align]
[align=left]//参数为空引用时引发此异常[/align]
[align=left]System.Console.WriteLine(exp.Message);[/align]
[align=left]}[/align]
[align=left]catch(System.NotSupportedExceptionexp)[/align]
[align=left]{[/align]
[align=left]//ArrayList为只读或具有固定大小时引发此异常[/align]
[align=left]System.Console.WriteLine(exp.Message);[/align]
[align=left]}[/align]
[align=left][/align]
[align=left]System.Console.WriteLine("把队列添加到ArrayList末尾之后!");[/align]
[align=left]//锁定[/align]
[align=left]lock(alsync.SyncRoot)[/align]
[align=left]{[/align]
[align=left]//枚举[/align]
[align=left]foreach(objectiteminal)[/align]
[align=left]{[/align]
[align=left]System.Console.WriteLine(item);[/align]
[align=left]}[/align]
[align=left]System.Console.WriteLine();[/align]
[align=left]}[/align]
BinarySearch方法可以在ArrayList里查找特定的元素,并且返回该元素的位置索引(索引从0开始)。如果value还是ArrayList的元素都不实现IComparable接口,则引发ArgumentException异常。如果value与ArrayList的元素类型不同,则引发InvalidOperationException异常。
[align=left]//查找“lazy”对象的索引[/align]
[align=left]alsync.Sort();//如果不进行排序结果有可能不正确[/align]
[align=left]lock(alsync.SyncRoot)[/align]
[align=left]{[/align]
[align=left]foreach(objectiteminalsync)[/align]
[align=left]{[/align]
[align=left]System.Console.WriteLine(item);[/align]
[align=left]}[/align]
[align=left]}[/align]
[align=left]try[/align]
[align=left]{[/align]
[align=left]intindex=alsync.BinarySearch("dog");[/align]
[align=left]System.Console.WriteLine("{0}",index);[/align]
[align=left]}[/align]
[align=left]catch(System.ArgumentExceptionexp)[/align]
[align=left]{[/align]
[align=left]System.Console.WriteLine(exp.Message);[/align]
[align=left]}[/align]
[align=left]catch(System.InvalidOperationExceptionexp)[/align]
[align=left]{[/align]
[align=left]//value与ArrayList的元素类型不同引发该异常[/align]
[align=left]System.Console.WriteLine(exp.Message);[/align]
[align=left]}[/align]
如果不对ArrayList进行排序则结果可能不正确。所以在调用BinarySearch方法之前必须调用ArrayList.Sort()方法。
方法GetEnumerator()也是非常有用的。可以这样使用这个方法:
[align=left]IEnumeratori=alsync.GetEnumerator();[/align]
[align=left]while(i.MoveNext())[/align]
[align=left]{[/align]
[align=left]System.Console.WriteLine(i.Current);[/align]
}
这样就枚举了一遍alsync的全部元素。
Hashtable类
Hashtable类是要在这里介绍的第二个常用的集合类。Hashtable表示键/值对的集合,这些键/值对根据键的哈希代码进行组织。DictionaryEntry定义了可设置或检索的字典键值对。而Hashtable的元素就是存储在DictionaryEntry里面的键值对。Hashtable中的键不能为空引用,但是值可以。Hashtable还对键作出了一些必要的要求。作为Hashtable中的键的对象,首先必须实现或者继承Object.GetHashCode和Object.Equals方法。如果键的相等性只是引用相等性,那么这些方法的继承实现就满足需要了。只要键对象用作Hashtable的键对象,就必须是永远不变的。当把元素添加到Hashtable时,根据键的哈希代码将该元素放入存储桶中。该键的后续查找将使用键的哈希代码只在一个特定存储桶中搜索,这样就大大减少为查找一个元素所需的键比较的次数。Hashtable的加载因子确定元素与存储桶的最大比率。加载因子越小,平均查找速度越快,但消耗的内存也增加。默认的加载因子1.0通常提供速度和大小之间的最佳平衡。当创建Hashtable时,也可以指定其他加载因子。当向Hashtable添加元素时,Hashtable的实际加载因子将增加。当实际加载因子达到此加载因子时,Hashtable中存储桶的数目自动增加到大于当前Hashtable存储桶数两倍的最小质数。Hashtable中的每个键对象必须提供其自己的哈希函数,可通过调用GetHash访问该函数。但是,可将任何实现IHashCodeProvider的对象传递到Hashtable构造函数,而且该哈希函数用于该表中的所有对象。
说了一大堆头都大了,不如先来看一个简单的例子吧!
[align=left]staticvoidMain(string[]args)[/align]
[align=left]{[/align]
[align=left]//创建一个Hashtable对象[/align]
[align=left]Hashtableht=newHashtable();[/align]
[align=left]//向Hashtalbe对象添加元素[/align]
[align=left]ht.Add("第一个","first");[/align]
[align=left]ht.Add("第二个","first");[/align]
[align=left]ht.Add("第三个","first");[/align]
[align=left]ht.Add("第四个","first");[/align]
[align=left][/align]
[align=left]//输出Hashtable对象的全部元素的键值对[/align]
[align=left]//这里必须使用IDictionaryEnumerator对象[/align]
[align=left]IDictionaryEnumeratori=ht.GetEnumerator();[/align]
[align=left]while(i.MoveNext())[/align]
[align=left]{[/align]
[align=left]System.Console.WriteLine("{0}{1}",i.Key,i.Value);[/align]
[align=left]}[/align]
[align=left][/align]
[align=left]System.Console.Read();[/align]
}
怎么样这个例子很简单吧。一目了然。没有什么好解释的。这样就可以简单地使用Hashtable对象了。但是要深入了解它还是不能放过它的方法和属性。
Hashtable的属性不多,而且和ArrayList的许多属性很相似。比如,Count属性,用来获取包含在Hashtable中的键值对的数目。又比如,FixedSize属性、IsReadOnly属性、IsSynchronized属性、SyncRoot属性的用法其实和ArrayList的类似属性基本一样。但是我还是非常愿意在这里再一次强调同步和线程安全的问题(尽管我已经在前面进行了详细的探讨)。对集合对象进行枚举,从根本上说,不是一个线程安全的过程。因为在对集合进行同步处理时其它线程有可能修改该集合,从而导致枚举数引发异常。解决的办法有两个,一是当其它线程进行更改而引发异常时捕捉到该异常,另一种更为简单的办法就是,在整个枚举过程中锁定集合。
[align=left]//创建一个Hashtable对象[/align]
[align=left]Hashtableht=newHashtable();[/align]
[align=left]//向Hashtalbe对象添加元素[/align]
[align=left]ht.Add("第一个","first");[/align]
[align=left]ht.Add("第二个","first");[/align]
[align=left]ht.Add("第三个","first");[/align]
[align=left]ht.Add("第四个","first");[/align]
[align=left][/align]
[align=left]//输出Hashtable对象的全部元素的键值对[/align]
[align=left]//这里必须使用IDictionaryEnumerator对象[/align]
[align=left]lock(ht.SyncRoot)[/align]
[align=left]{[/align]
[align=left]IDictionaryEnumeratori=ht.GetEnumerator();[/align]
[align=left]while(i.MoveNext())[/align]
[align=left]{[/align]
[align=left]System.Console.WriteLine("{0}{1}",i.Key,i.Value);[/align]
[align=left]}[/align]
}
但是仅仅这样还是不够的,因为这只能保证在枚举的过程当中的线程安全。可是要保证对于集合类的所有操作都是线程安全的就必须使用集合类的同步包装。我们在上面示例代码的基础上加入使用同步包装的语句,形成下面的程序。
[align=left]//创建一个Hashtable对象[/align]
[align=left]Hashtableht=newHashtable();[/align]
[align=left]//创建线程包装[/align]
[align=left]Hashtablesyncht=Hashtable.Synchronized(ht);[/align]
[align=left]//向Hashtalbe对象添加元素[/align]
[align=left]syncht.Add("第一个","first");[/align]
[align=left]syncht.Add("第二个","first");[/align]
[align=left]syncht.Add("第三个","first");[/align]
[align=left]syncht.Add("第四个","first");[/align]
[align=left][/align]
[align=left]//输出Hashtable对象的全部元素的键值对[/align]
[align=left]//这里必须使用IDictionaryEnumerator对象[/align]
[align=left]lock(ht.SyncRoot)[/align]
[align=left]{[/align]
[align=left]IDictionaryEnumeratori=syncht.GetEnumerator();[/align]
[align=left]while(i.MoveNext())[/align]
[align=left]{[/align]
[align=left]System.Console.WriteLine("{0}{1}",i.Key,i.Value);[/align]
[align=left]}[/align]
}
在任何时候保证使用集合类型对象的线程包装进行所有操作,就可以保证对于集合的任何正常操作都是安全的。
Hashtable的方法起初一看和ArrayList的方法也非常相似。但是还是有不少细节需要讨论。就拿这个Add()方法来说吧。一看就知道它和ArrayList.Add()方法一样是用来向Hashtable里面添加元素的,所不同的是它要求两个参数:
Hashtable.Add(objectkey,objectvalue)
参数key,表示要添加的元素的键,而参数value则表示要添加的元素的值。特别强调,Hashtable中的键是不能接受空引用的,而值可以是空引用。另外在这里对键还有一些要求。作为Hashtable中的键的对象首先必须实现或者继承Object.GetHashCode和Object.Equals方法。如果键的相等性只是引用相等性,那么这些方法的继承实现就满足需要了。也就是说大多数时候,只要是继承了Object类型的GetHashCode方法和Equals方法的类型,都可以作为Hashtable的元素的键使用。可以这样调用Hashtable.Add()方法。
[align=left]//向Hashtalbe对象添加元素[/align]
[align=left]syncht.Add("第一个","first");[/align]
[align=left]syncht.Add("第二个","first");[/align]
[align=left]syncht.Add("第三个","first");[/align]
[align=left]syncht.Add("第四个","first");[/align]
[align=left]syncht.Add(5,"五");[/align]
syncht.Add(28.852,6);
如果参数key为空引用,该方法将会引发ArgumentNullException异常。
另外,Hashtable中值得注意的两个方法是ContainsKey方法和ContainsValue方法,前者用来判断Hashtable中是否含有特定键,而后者则判断Hashtable中是否含有特定值。如果包含特定的键或值,则方法返回true,否则返回false。
Stack类
堆栈类是我要介绍的第三个常用集合类。我想堆栈不用我再费口舌。该类表示对象的简单的后进先出集合。Stack接受空引用作为有效值,并且允许重复值。照例,在介绍该类的之前先来看一个简短的程序例子。
[align=left]//创建stack对象[/align]
[align=left]Stacks=newStack();[/align]
[align=left]//返回stack对象的包装[/align]
[align=left]Stacksyncs=Stack.Synchronized(s);[/align]
[align=left][/align]
[align=left]//压栈[/align]
[align=left]syncs.Push("a");[/align]
[align=left]syncs.Push("b");[/align]
[align=left]syncs.Push("c");[/align]
[align=left][/align]
[align=left]//锁定堆栈[/align]
[align=left]lock(syncs.SyncRoot)[/align]
[align=left]{[/align]
[align=left]//枚举[/align]
[align=left]IEnumeratori=syncs.GetEnumerator();[/align]
[align=left]while(i.MoveNext())[/align]
[align=left]{[/align]
[align=left]System.Console.WriteLine(i.Current);[/align]
[align=left]}[/align]
[align=left]}[/align]
[align=left][/align]
[align=left]//弹栈[/align]
[align=left]while(syncs.Count>0)[/align]
[align=left]{[/align]
[align=left]System.Console.WriteLine(syncs.Pop());[/align]
}
Stack类型的属性恐怕是所有集合类里面最简单的。只有三个,Count、IsSynchronized和SyncRoot。我们应该都很熟悉了。不许要详细叙述。而在Stack的为数不多的方法里只有Push、Pop和Peek三个方法是独特的。Push方法将对象压入堆栈的顶部。该方法只要求一个Object类型的参数。调用该方法会使Stack.Count加1。Pop方法把栈顶的元素弹出。该方法不要求参数,只有一个object类型的返回值。调用此方法过后,Stack.Count值减1。最后,Peek方法可以返回堆栈顶部的对象,但是并不把该对象移除。也就是说,调用此方法过后Stack.Count值不变。当堆栈为空时调用Pop、Peek方法会引发InvalidOperationException异常。
Queue类
队列是最后一个要为大家介绍的常用集合类型。关于什么是队列我实在不想多说了,我想大家都知道。而Queue类实现的正式这样一种对象的先进先出集合。这个类型实在是平淡无奇。我只介绍三个方法,分别是,Dequeue、Enqueue、Peek方法。Enqueue是入队操作,该方法将对象添加到队列尾部。唯一的参数要求传递一个object类型的对象。Dequeue是出队操作,该方法移除并返回队头对象。Peek方法返回队头对象,但是不将其移除。如果队列为空,那么调用Peek或Dequeue方法会引发InvalidOperationException异常。
创建类型严格的集合
在命名空间System.Collections当中有一个类型是专门提供给开发者用来建立自己的类型严格的集合的。这个类就是CollectionBase类。该类为强类型集合提供抽象基类。System.Collections命名空间提供此基类使开发者能够非常容易的创建自定义集合类型。开发者应该继承此类并加以扩充,而不建议创建自己的基类。CollectionBase类只有一个公共属性Count。该属性获取包含在CollectionBase实例中的元素个数。另外还有两个受保护属性提供给其子类。其中InnerList属性用于获取一个ArrayList,它包含CollectionBase实例中元素的列表。另一个List属性,用来获取一个IList,它同样包含CollectionBase实例中元素的列表。下面这段程序从CollectionBase类派生出名为Int16Collection的集合类型。
[align=left]staticvoidMain(string[]args)[/align]
[align=left]{[/align]
[align=left]Int16Collectioni=newInt16Collection();[/align]
[align=left][/align]
[align=left]i.Add(16);[/align]
[align=left]i.Add(28);[/align]
[align=left]i.Add(256);[/align]
[align=left][/align]
[align=left]IEnumeratorie=i.GetEnumerator();[/align]
[align=left]while(ie.MoveNext())[/align]
[align=left]{[/align]
[align=left]System.Console.WriteLine(ie.Current);[/align]
[align=left]}[/align]
[align=left][/align]
[align=left]System.Console.WriteLine(i.IndexOf(256));[/align]
[align=left][/align]
[align=left]System.Console.Read();[/align]
[align=left]}[/align]
[align=left]}[/align]
[align=left][/align]
[align=left]publicclassInt16Collection:CollectionBase[/align]
[align=left]{[/align]
[align=left]publicInt16Collection()[/align]
[align=left]{[/align]
[align=left]}[/align]
[align=left][/align]
[align=left]///<summary>[/align]
[align=left]///Int16Collection集合元素[/align]
[align=left]///</summary>[/align]
[align=left]publicInt16this[intindex][/align]
[align=left]{[/align]
[align=left]get[/align]
[align=left]{[/align]
[align=left]return(Int16)this.List[index];[/align]
[align=left]}[/align]
[align=left]set[/align]
[align=left]{[/align]
[align=left]this.List[index]=value;[/align]
[align=left]}[/align]
[align=left]}[/align]
[align=left][/align]
[align=left]publicintAdd(Int16value)[/align]
[align=left]{[/align]
[align=left]returnthis.List.Add(value);[/align]
[align=left]}[/align]
[align=left][/align]
[align=left]publicintIndexOf(Int16value)[/align]
[align=left]{[/align]
[align=left]returnthis.List.IndexOf(value);[/align]
[align=left]}[/align]
[align=left][/align]
[align=left]publicvoidInsert(intindex,Int16value)[/align]
[align=left]{[/align]
[align=left]this.List.Insert(index,value);[/align]
[align=left]}[/align]
[align=left][/align]
[align=left]publicvoidRemove(Int16value)[/align]
[align=left]{[/align]
[align=left]this.List.Remove(value);[/align]
[align=left]}[/align]
}
-----------
SortedList类类似于Hashtable和ArrayList间的混合。
与Hashtable一样,SortedList也基于IDictionary接口;因此,SortedList的每一元素都是键和值对。SortedList提供只返回键列表或只返回值列表的方法。
与ArrayList一样,SortedList是元素序列。它被索引,并且根据特定的比较器被排序。
SortedList在所有Collections类中是唯一的,在其中每一元素都可通过三种方式访问:使用键、值或索引。
如果您想要一个保存键/值对的集合并且还需要索引列表的灵活性,请使用SortedList。
System.Collections命名空间包含接口和类,这些接口和类定义各种对象(如,列表、队列、位数组、哈希表和字典)的集合。它们主要有三种类型:第一组类型是一组接口,用来定义集合必须遵守的一系列协定。第二组类型包括一组常用集合类型。例如:ArrayList、Hashtable、Stack、Queue。我们可以非常方便地使用这些来检索和存储内存中的数据。第三组类型用来支持类型严格的集合的创建。通过CollectionBase类提供数据存储,我们可以方便地创建自定义集合而且是类型严格的——这一点很重要。
下面我们逐一深入探讨上面提到的三组类型。首先,我们要全面了解一下命名空间里面的接口,看一看这些接口能够为我们提供哪些协定。然后讨论如何使用常用的集合类型。最后讨论利用CollectionBase类创建自定义的集合类型。
集合接口
System.Collections.ICollection接口
该接口定义了所有集合的大小、枚举数和同步方法。
System.Collections.IEnumerable
System.Collections.ICollection
该接口有如下成员:
Count属性 | 通过类实现时,获取ICollection中包含的元素数。 |
IsSynchronized属性 | 通过类实现时,获取一个值,指示对ICollection的访问是否同步(线程安全)。 |
SyncRoot属性 | 通过类实现时,获取可用于同步对ICollection的访问的对象。 |
CopyTo方法 | 通过类实现时,从特定Array索引处开始将ICollection的元素复制到Array中。 |
该接口公开了比较两个对象的方法。默认实现为Comparer类。该接口只有一个成员,就是Compare方法。该方法比较两个对象并返回一个值,指示一个对象是小于、等于还是大于另一个对象。如果比较两个对象X和Y。
小于0 | X<Y |
等于0 | X=Y |
大于0 | X>Y |
该接口表示键/值对的集合。具有以下属性和方法:
IsFixedSize | 通过类实现时,该值指示IDictionary是否具有固定大小。 |
IsReadOnly | 通过类实现时,获取一个指示IDictionary是否为只读的值。 |
Item | 通过类实现时,获取或设置带有指定键的元素。 在C#中,该属性为IDictionary类的索引器。 |
Keys | 通过类实现时,获取包含IDictionary的键的ICollection。 |
Values | 通过类实现时,获取包含IDictionary中的值的ICollection。 |
Add | 通过类实现时,向IDictionary添加带有所提供的键和值的元素。 |
Clear | 通过类实现时,从IDictionary移除所有元素。 |
Contains | 通过类实现时,确定IDictionary是否包含带有指定键的元素。 |
GetEnumerator | 通过类实现时,返回IDictionary的IdictionaryEnumerator。 |
Remove | 通过类实现时,从IDictionary中移除带有指定键的元素。 |
该接口公开枚举数,该枚举数支持在集合上进行简单迭代。方法GetEnumerator是该接口唯一的公共方法。返回可循环访问集合的枚举数。
System.Collections.IEnumerator接口
该接口支持在集合上进行简单迭代。其属性Current用于获取集合中的当前元素。方法MoveNext()方法将枚举数推进到下一个元素。方法Reset()将枚举数设置为其初始位置,该位置位于集合中第一个元素之前。
System.Collections.IhashCodeProvider接口
使用自定义哈希函数为对象提供哈希代码。方法GetHashCode()返回指定对象的哈希代码。
System.Collections.Ilist接口
该接口表示可按照索引单独访问的一组对象。其属性包括:
IsFixedSize | 通过类实现时,获取一个值,该值指示IList是否具有固定大小。 |
IsReadOnly | 通过类实现时,获取一个指示IList是否为只读的值。 |
Item | 通过类实现时,获取或设置在指定的索引处的元素。在C#中,该属性为IList类的索引器。 |
Add | 通过类实现时,将一项添加到IList。 |
Clear | 通过类实现时,从IList中移除所有项。 |
ConTains | 通过类实现时,确定IList是否包含特定的值。 |
IndexOf | 通过类实现时,确定IList中特定项的索引。 |
Insert | 通过类实现时,在IList的指定位置处插入一项。 |
Remove | 通过类实现时,从IList移除特定对象的第一个匹配项。 |
RemoveAt | 通过类实现时,在指定的索引处移除IList项。 |
常用集合类型
常用集合类型主要包括:ArrayList、Hashtable、Stack、Queue类。
System.Collections.ArrayList类
该类实现了IList接口。该类的容量,也即该列表所能包含的元素数,其大小可按需动态增加。可通过调用TrimToSize()或Capacity属性减少容量。该类的元素索引从0开始,即列表中的头一个元素的索引是0。该类允许重复元素并且接受空引用。示例:
usingSystem;
usingSystem.Collections;
publicclassSamplesArrayList{
publicstaticvoidMain(){
//创建并初始化ArryList对象
ArrayListmyAL=newArrayList();
myAL.Add("Hello");
myAL.Add("World");
myAL.Add("!");
//显示ArryList对象的属性和值
Console.WriteLine("myAL");
Console.WriteLine("/tCount:{0}",myAL.Count);
Console.WriteLine("/tCapacity:{0}",myAL.Capacity);
Console.Write("/tValues:");
PrintValues(myAL);
}
publicstaticvoidPrintValues(IEnumerablemyList){
System.Collections.IEnumeratormyEnumerator=myList.GetEnumerator();
while(myEnumerator.MoveNext())
Console.Write("/t{0}",myEnumerator.Current);
Console.WriteLine();
}
}
/*
正确的输出
myAL
Count:3
Capacity:16
Values:HelloWorld!
*/
ArryList类包含的主要属性有:
Capacity | ArrayList可以包含的元素数。Capacity是ArrayList可以存储的元素数。Count是ArrayList中实际包含的元素数。Capacity总是大于或等于Count。如果在添加元素时,Count超过Capacity,则该列表的容量会通过自动重新分配内部数组加倍。如果Capacity的值显式设置,则内部数组也需要重新分配以容纳指定的容量。如果Capacity被显式设置为0,则公共语言运行库将其设置为默认容量。默认容量为16。 |
Count | ArrayList现在实际包含的元素数。 |
IsFixedSize | 指定ArrayList是否具有固定大小。如果ArrayList具有固定大小,则为true;否则为false。默认值为false。大小固定的集合在创建之后不允许添加或移除元素,但允许修改现有元素。固定大小的集合只是一个具有用于防止添加和移除元素的包装的集合;因此,如果更改基础集合(包括添加和移除元素),则固定大小的集合将反映那些更改。 |
IsReadOnly | 指定ArrayList是否是只读的。如果ArrayListt为只读,则为true;否则为false。默认值为false。只读集合在创建之后不允许添加、移除或修改元素。只读集合只是一个具有用于防止修改的包装的集合;因此,如果更改基础集合,则只读集合将反映那些更改。 |
IsSynchronized | 指示对ArrayList的访问是否同步。如果对ArrayList的访问是同步(线程安全)的,则为true;否则为false。默认值为false。若要保证ArrayList的线程安全,则必须通过由Synchronized属性返回的包装来执行所有操作。 |
Item | 指定索引处的元素在。C#中,该属性为ArrayList类的索引器。ArrayList接受空引用(VisualBasic中为Nothing)作为有效值并且允许重复的元素。 |
SyncRoot | 获取可用于同步对ArrayList访问的对象。若要创建 |
[align=left]classClass1[/align]
[align=left]{[/align]
[align=left][STAThread][/align]
[align=left]staticvoidMain(string[]args)[/align]
[align=left]{[/align]
[align=left]ArrayListal=newArrayList();[/align]
[align=left]al.Add("aaa");[/align]
[align=left]al.Add("bbb");[/align]
[align=left]al.Add("ccc");[/align]
[align=left]al.Add("ddd");[/align]
[align=left]lock(al.SyncRoot)[/align]
[align=left]{[/align]
[align=left]foreach(objectiteminal)[/align]
[align=left]{[/align]
[align=left]System.Console.WriteLine(item);[/align]
[align=left]}[/align]
[align=left]}[/align]
[align=left][/align]
[align=left]System.Console.Read();[/align]
}
}
但是仅仅在枚举过程中锁定ArrayList还是不够的,如果希望线程安全地使用ArrayList就必须使用ArrayList.Synchronized方法所返回的包装来完成所有操作。如下例:
[align=left]usingSystem;[/align]
[align=left]usingSystem.Collections;[/align]
[align=left][/align]
[align=left]namespaceConsoleApplication1[/align]
[align=left]{[/align]
[align=left]///<summary>[/align]
[align=left]///Class1的摘要说明。[/align]
[align=left]///</summary>[/align]
[align=left]classClass1[/align]
[align=left]{[/align]
[align=left][STAThread][/align]
[align=left]staticvoidMain(string[]args)[/align]
[align=left]{[/align]
[align=left]ArrayListal=newArrayList();//创建一个ArrayList[/align]
[align=left]ArrayListalsync=ArrayList.Synchronized(al);//为al创建一个线程安全的包装[/align]
[align=left]if(al.IsSynchronized==true)[/align]
[align=left]{[/align]
[align=left]System.Console.WriteLine("al是线程安全的。");[/align]
[align=left]}[/align]
[align=left]else[/align]
[align=left]{[/align]
[align=left]System.Console.WriteLine("al不是线程安全的。");[/align]
[align=left]}[/align]
[align=left]if(alsync.IsSynchronized==true)[/align]
[align=left]{[/align]
[align=left]System.Console.WriteLine("alsync是线程安全的。");[/align]
[align=left]}[/align]
[align=left]else[/align]
[align=left]{[/align]
[align=left]System.Console.WriteLine("alsync不是线程安全的。");[/align]
[align=left]}[/align]
[align=left]//添加列表元素[/align]
[align=left]alsync.Add("aaa");[/align]
[align=left]alsync.Add("bbb");[/align]
[align=left]alsync.Add("ccc");[/align]
[align=left]alsync.Add("ddd");[/align]
[align=left]alsync.Add("eee");[/align]
[align=left]//锁定[/align]
[align=left]lock(alsync.SyncRoot)[/align]
[align=left]{[/align]
[align=left]//枚举[/align]
[align=left]foreach(objectiteminal)[/align]
[align=left]{[/align]
[align=left]System.Console.WriteLine(item);[/align]
[align=left]}[/align]
[align=left]System.Console.WriteLine();[/align]
[align=left]}[/align]
[align=left]//枚举[/align]
[align=left]IEnumeratori=alsync.GetEnumerator();[/align]
[align=left]while(i.MoveNext())[/align]
[align=left]{[/align]
[align=left]System.Console.WriteLine(i.Current);[/align]
[align=left]}[/align]
[align=left][/align]
[align=left]System.Console.Read();[/align]
[align=left]}[/align]
[align=left]}[/align]
[align=left]}[/align]
[align=left][/align]
[align=left]除了属性之外ArrayList还有一大堆方法需要了解:[/align]
[align=left]Adapter[/align] | [align=left]为特定的IList创建ArrayList包装[/align] |
[align=left]Add[/align] | [align=left]将对象添加到ArrayList的结尾处。[/align] |
[align=left]AddRange[/align] | [align=left]将ICollection的元素添加到ArrayList的末尾。[/align] |
[align=left]BinarySearch[/align] | [align=left]使用对分检索算法在已排序的ArrayList或它的一部分中查找特定元素。[/align] |
[align=left]Clear[/align] | [align=left]从ArrayList中移除所有元素[/align] |
[align=left]Clone[/align] | [align=left]创建ArrayList的浅表副本。集合的浅表副本仅复制集合的元素(不论它们是引用类型还是值类型),但不复制引用所引用的对象。新集合中的引用与原始集合中的引用指向相同的对象。与之相对,集合的深层副本将复制这些元素以及由它们直接或间接引用的所有内容。[/align] |
[align=left]Contains[/align] | 确定某个元素是否在ArrayList中。如果在ArrayList中找到item,则为true;否则为false。 |
[align=left]CopyTo[/align] | [align=left]将ArrayList或它的一部分复制到一维数组中。[/align] |
[align=left]Equals[/align] | [align=left]确定两个Object实例是否相等。[/align] |
[align=left]FixedSize[/align] | [align=left]返回具有固定大小的列表包装,其中的元素允许修改,但不允许添加或移除。[/align] |
[align=left]GetEnumerator[/align] | [align=left]返回可循环访问ArrayList的枚举数。[/align] |
[align=left]GetHashCode[/align] | [align=left]用作特定类型的哈希函数,适合在哈希算法和数据结构(如哈希表)中使用。[/align] |
[align=left]GetRange[/align] | [align=left]返回ArrayList,它表示源ArrayList中元素的子集。[/align] |
[align=left]GetType[/align] | [align=left]获取当前实例的Type。[/align] |
[align=left]IndexOf[/align] | [align=left]返回ArrayList或它的一部分中某个值的第一个匹配项的从零开始的索引。[/align] |
[align=left]Insert[/align] | [align=left]将元素插入ArrayList的指定索引处。[/align] |
[align=left]InsertRange[/align] | [align=left]将集合中的某个元素插入ArrayList的指定索引处。[/align] |
[align=left]LastIndexOf[/align] | [align=left]返回ArrayList或它的一部分中某个值的最后一个匹配项的从零开始的索引。[/align] |
[align=left]ReadOnly[/align] | [align=left]返回只读的列表包装。[/align] |
[align=left]Remove[/align] | [align=left]从ArrayList中移除特定对象的第一个匹配项。[/align] |
[align=left]RemoveAt[/align] | [align=left]移除ArrayList的指定索引处的元素。[/align] |
[align=left]RemoveRange[/align] | [align=left]从ArrayList中移除一定范围的元素。[/align] |
[align=left]Repeat[/align] | [align=left]返回ArrayList,它的元素是指定值的副本。[/align] |
[align=left]Reverse[/align] | [align=left]将ArrayList或它的一部分中元素的顺序反转。[/align] |
[align=left]SetRange[/align] | [align=left]将集合中的元素复制到ArrayList中一定范围的元素上。[/align] |
[align=left]Sort[/align] | [align=left]对ArrayList或它的一部分中的元素进行排序。[/align] |
[align=left]Synchronized[/align] | [align=left]返回同步(线程安全)的列表包装。[/align] |
[align=left]ToArray[/align] | [align=left]将ArrayList的元素复制到新数组中。[/align] |
[align=left]ToString[/align] | [align=left]返回表示当前Object的String。[/align] |
[align=left]TrimToSize[/align] | [align=left]将容量设置为ArrayList中元素的实际数量。[/align] |
这些方法看上去都不复杂。不过我们还是一起来研究几个有意思或者常用的方法。首先来看一下这个Add方法吧。该方法可以在ArrayList的结尾处添加一个新的对象。唯一的参数Value用来接受要加到ArrayList末尾对象。
[align=left]try[/align]
[align=left]{[/align]
[align=left]//添加列表元素[/align]
[align=left]//唯一的参数Value用来接受要加到ArrayList末尾对象。[/align]
[align=left]alsync.Add("aaa");[/align]
[align=left]alsync.Add("bbb");[/align]
[align=left]alsync.Add("ccc");[/align]
[align=left]//ArrayList允许接受重复元素[/align]
[align=left]alsync.Add("ddd");[/align]
[align=left]alsync.Add("ddd");[/align]
[align=left]alsync.Add("eee");[/align]
[align=left]alsync.Add("ffff");[/align]
[align=left]//ArrayList接受空引用[/align]
[align=left]alsync.Add(null);[/align]
[align=left]}[/align]
[align=left]catch(System.NotSupportedExceptionexp)[/align]
[align=left]{[/align]
[align=left]//ArrayList为只读,或者有固定大小将会引发此异常[/align]
[align=left]System.Console.WriteLine(exp.Message);[/align]
[align=left]}[/align]
[align=left][/align]
该方法可能引发System.NotSupportedException异常。如果ArrayList为只读,或者有固定大小将会引发此异常。而AddRange方法与Add方法有所不同。该方法把ICollection的元素添加到ArrayList的末尾。唯一的参数c是ICollection接口类型,其元素会被添加到ArrayList的末尾。该参数如果为空引用则引发ArgumentNullException异常。如果ArrayList为只读或者具有固定大小则引发NotSupportedException异常。下例把一个Queue添加到ArrayList的末尾。
[align=left]staticvoidMain(string[]args)[/align]
[align=left]{[/align]
[align=left]ArrayListal=newArrayList();//创建一个ArrayList[/align]
[align=left]ArrayListalsync=ArrayList.Synchronized(al);//为al创建一个线程安全的包装[/align]
[align=left][/align]
[align=left]try[/align]
[align=left]{[/align]
[align=left]//添加列表元素[/align]
[align=left]//唯一的参数Value用来接受要加到ArrayList末尾对象。[/align]
[align=left]alsync.Add("aaa");[/align]
[align=left]alsync.Add("bbb");[/align]
[align=left]alsync.Add("ccc");[/align]
[align=left]//ArrayList允许接受重复元素[/align]
[align=left]alsync.Add("ddd");[/align]
[align=left]alsync.Add("ddd");[/align]
[align=left]alsync.Add("eee");[/align]
[align=left]alsync.Add("ffff");[/align]
[align=left]//ArrayList接受空引用[/align]
[align=left]alsync.Add(null);[/align]
[align=left]}[/align]
[align=left]catch(System.NotSupportedExceptionexp)[/align]
[align=left]{[/align]
[align=left]//ArrayList为只读,或者有固定大小将会引发此异常[/align]
[align=left]System.Console.WriteLine(exp.Message);[/align]
[align=left]}[/align]
[align=left]System.Console.WriteLine("没有把队列加到ArrayList之前!");[/align]
[align=left]IEnumeratori=alsync.GetEnumerator();[/align]
[align=left]while(i.MoveNext())[/align]
[align=left]{[/align]
[align=left]System.Console.WriteLine(i.Current);[/align]
[align=left]}[/align]
[align=left][/align]
[align=left]//创建并初始化一个队列[/align]
[align=left]QueuemyQueue=newQueue();[/align]
[align=left]myQueue.Enqueue("jumped");[/align]
[align=left]myQueue.Enqueue("over");[/align]
[align=left]myQueue.Enqueue("the");[/align]
[align=left]myQueue.Enqueue("lazy");[/align]
[align=left]myQueue.Enqueue("dog");[/align]
[align=left][/align]
[align=left]//把队列添加到ArrayList的末尾[/align]
[align=left]try[/align]
[align=left]{[/align]
[align=left]alsync.AddRange(myQueue);[/align]
[align=left]}[/align]
[align=left]catch(System.ArgumentNullExceptionexp)[/align]
[align=left]{[/align]
[align=left]//参数为空引用时引发此异常[/align]
[align=left]System.Console.WriteLine(exp.Message);[/align]
[align=left]}[/align]
[align=left]catch(System.NotSupportedExceptionexp)[/align]
[align=left]{[/align]
[align=left]//ArrayList为只读或具有固定大小时引发此异常[/align]
[align=left]System.Console.WriteLine(exp.Message);[/align]
[align=left]}[/align]
[align=left][/align]
[align=left]System.Console.WriteLine("把队列添加到ArrayList末尾之后!");[/align]
[align=left]//锁定[/align]
[align=left]lock(alsync.SyncRoot)[/align]
[align=left]{[/align]
[align=left]//枚举[/align]
[align=left]foreach(objectiteminal)[/align]
[align=left]{[/align]
[align=left]System.Console.WriteLine(item);[/align]
[align=left]}[/align]
[align=left]System.Console.WriteLine();[/align]
[align=left]}[/align]
BinarySearch方法可以在ArrayList里查找特定的元素,并且返回该元素的位置索引(索引从0开始)。如果value还是ArrayList的元素都不实现IComparable接口,则引发ArgumentException异常。如果value与
[align=left]//查找“lazy”对象的索引[/align]
[align=left]alsync.Sort();//如果不进行排序结果有可能不正确[/align]
[align=left]lock(alsync.SyncRoot)[/align]
[align=left]{[/align]
[align=left]foreach(objectiteminalsync)[/align]
[align=left]{[/align]
[align=left]System.Console.WriteLine(item);[/align]
[align=left]}[/align]
[align=left]}[/align]
[align=left]try[/align]
[align=left]{[/align]
[align=left]intindex=alsync.BinarySearch("dog");[/align]
[align=left]System.Console.WriteLine("{0}",index);[/align]
[align=left]}[/align]
[align=left]catch(System.ArgumentExceptionexp)[/align]
[align=left]{[/align]
[align=left]System.Console.WriteLine(exp.Message);[/align]
[align=left]}[/align]
[align=left]catch(System.InvalidOperationExceptionexp)[/align]
[align=left]{[/align]
[align=left]//value与ArrayList的元素类型不同引发该异常[/align]
[align=left]System.Console.WriteLine(exp.Message);[/align]
[align=left]}[/align]
如果不对ArrayList进行排序则结果可能不正确。所以在调用BinarySearch方法之前必须调用ArrayList.Sort()方法。
方法GetEnumerator()也是非常有用的。可以这样使用这个方法:
[align=left]IEnumeratori=alsync.GetEnumerator();[/align]
[align=left]while(i.MoveNext())[/align]
[align=left]{[/align]
[align=left]System.Console.WriteLine(i.Current);[/align]
}
这样就枚举了一遍alsync的全部元素。
Hashtable类
Hashtable类是要在这里介绍的第二个常用的集合类。Hashtable表示键/值对的集合,这些键/值对根据键的哈希代码进行组织。DictionaryEntry定义了可设置或检索的字典键值对。而Hashtable的元素就是存储在DictionaryEntry里面的键值对。Hashtable中的键不能为空引用,但是值可以。Hashtable还对键作出了一些必要的要求。作为Hashtable中的键的对象,首先必须实现或者继承Object.GetHashCode和Object.Equals方法。如果键的相等性只是引用相等性,那么这些方法的继承实现就满足需要了。只要键对象用作Hashtable的键对象,就必须是永远不变的。当把元素添加到Hashtable时,根据键的哈希代码将该元素放入存储桶中。该键的后续查找将使用键的哈希代码只在一个特定存储桶中搜索,这样就大大减少为查找一个元素所需的键比较的次数。Hashtable的加载因子确定元素与存储桶的最大比率。加载因子越小,平均查找速度越快,但消耗的内存也增加。默认的加载因子1.0通常提供速度和大小之间的最佳平衡。当创建Hashtable时,也可以指定其他加载因子。当向Hashtable添加元素时,Hashtable的实际加载因子将增加。当实际加载因子达到此加载因子时,Hashtable中存储桶的数目自动增加到大于当前Hashtable存储桶数两倍的最小质数。Hashtable中的每个键对象必须提供其自己的哈希函数,可通过调用GetHash访问该函数。但是,可将任何实现IHashCodeProvider的对象传递到Hashtable构造函数,而且该哈希函数用于该表中的所有对象。
说了一大堆头都大了,不如先来看一个简单的例子吧!
[align=left]staticvoidMain(string[]args)[/align]
[align=left]{[/align]
[align=left]//创建一个Hashtable对象[/align]
[align=left]Hashtableht=newHashtable();[/align]
[align=left]//向Hashtalbe对象添加元素[/align]
[align=left]ht.Add("第一个","first");[/align]
[align=left]ht.Add("第二个","first");[/align]
[align=left]ht.Add("第三个","first");[/align]
[align=left]ht.Add("第四个","first");[/align]
[align=left][/align]
[align=left]//输出Hashtable对象的全部元素的键值对[/align]
[align=left]//这里必须使用IDictionaryEnumerator对象[/align]
[align=left]IDictionaryEnumeratori=ht.GetEnumerator();[/align]
[align=left]while(i.MoveNext())[/align]
[align=left]{[/align]
[align=left]System.Console.WriteLine("{0}{1}",i.Key,i.Value);[/align]
[align=left]}[/align]
[align=left][/align]
[align=left]System.Console.Read();[/align]
}
怎么样这个例子很简单吧。一目了然。没有什么好解释的。这样就可以简单地使用Hashtable对象了。但是要深入了解它还是不能放过它的方法和属性。
Hashtable的属性不多,而且和ArrayList的许多属性很相似。比如,Count属性,用来获取包含在Hashtable中的键值对的数目。又比如,FixedSize属性、IsReadOnly属性、IsSynchronized属性、SyncRoot属性的用法其实和ArrayList的类似属性基本一样。但是我还是非常愿意在这里再一次强调同步和线程安全的问题(尽管我已经在前面进行了详细的探讨)。对集合对象进行枚举,从根本上说,不是一个线程安全的过程。因为在对集合进行同步处理时其它线程有可能修改该集合,从而导致枚举数引发异常。解决的办法有两个,一是当其它线程进行更改而引发异常时捕捉到该异常,另一种更为简单的办法就是,在整个枚举过程中锁定集合。
[align=left]//创建一个Hashtable对象[/align]
[align=left]Hashtableht=newHashtable();[/align]
[align=left]//向Hashtalbe对象添加元素[/align]
[align=left]ht.Add("第一个","first");[/align]
[align=left]ht.Add("第二个","first");[/align]
[align=left]ht.Add("第三个","first");[/align]
[align=left]ht.Add("第四个","first");[/align]
[align=left][/align]
[align=left]//输出Hashtable对象的全部元素的键值对[/align]
[align=left]//这里必须使用IDictionaryEnumerator对象[/align]
[align=left]lock(ht.SyncRoot)[/align]
[align=left]{[/align]
[align=left]IDictionaryEnumeratori=ht.GetEnumerator();[/align]
[align=left]while(i.MoveNext())[/align]
[align=left]{[/align]
[align=left]System.Console.WriteLine("{0}{1}",i.Key,i.Value);[/align]
[align=left]}[/align]
}
但是仅仅这样还是不够的,因为这只能保证在枚举的过程当中的线程安全。可是要保证对于集合类的所有操作都是线程安全的就必须使用集合类的同步包装。我们在上面示例代码的基础上加入使用同步包装的语句,形成下面的程序。
[align=left]//创建一个Hashtable对象[/align]
[align=left]Hashtableht=newHashtable();[/align]
[align=left]//创建线程包装[/align]
[align=left]Hashtablesyncht=Hashtable.Synchronized(ht);[/align]
[align=left]//向Hashtalbe对象添加元素[/align]
[align=left]syncht.Add("第一个","first");[/align]
[align=left]syncht.Add("第二个","first");[/align]
[align=left]syncht.Add("第三个","first");[/align]
[align=left]syncht.Add("第四个","first");[/align]
[align=left][/align]
[align=left]//输出Hashtable对象的全部元素的键值对[/align]
[align=left]//这里必须使用IDictionaryEnumerator对象[/align]
[align=left]lock(ht.SyncRoot)[/align]
[align=left]{[/align]
[align=left]IDictionaryEnumeratori=syncht.GetEnumerator();[/align]
[align=left]while(i.MoveNext())[/align]
[align=left]{[/align]
[align=left]System.Console.WriteLine("{0}{1}",i.Key,i.Value);[/align]
[align=left]}[/align]
}
在任何时候保证使用集合类型对象的线程包装进行所有操作,就可以保证对于集合的任何正常操作都是安全的。
Hashtable的方法起初一看和ArrayList的方法也非常相似。但是还是有不少细节需要讨论。就拿这个Add()方法来说吧。一看就知道它和ArrayList.Add()方法一样是用来向Hashtable里面添加元素的,所不同的是它要求两个参数:
Hashtable.Add(objectkey,objectvalue)
参数key,表示要添加的元素的键,而参数value则表示要添加的元素的值。特别强调,Hashtable中的键是不能接受空引用的,而值可以是空引用。另外在这里对键还有一些要求。作为Hashtable中的键的对象首先必须实现或者继承Object.GetHashCode和Object.Equals方法。如果键的相等性只是引用相等性,那么这些方法的继承实现就满足需要了。也就是说大多数时候,只要是继承了Object类型的GetHashCode方法和Equals方法的类型,都可以作为Hashtable的元素的键使用。可以这样调用Hashtable.Add()方法。
[align=left]//向Hashtalbe对象添加元素[/align]
[align=left]syncht.Add("第一个","first");[/align]
[align=left]syncht.Add("第二个","first");[/align]
[align=left]syncht.Add("第三个","first");[/align]
[align=left]syncht.Add("第四个","first");[/align]
[align=left]syncht.Add(5,"五");[/align]
syncht.Add(28.852,6);
如果参数key为空引用,该方法将会引发ArgumentNullException异常。
另外,Hashtable中值得注意的两个方法是ContainsKey方法和ContainsValue方法,前者用来判断Hashtable中是否含有特定键,而后者则判断Hashtable中是否含有特定值。如果包含特定的键或值,则方法返回true,否则返回false。
Stack类
堆栈类是我要介绍的第三个常用集合类。我想堆栈不用我再费口舌。该类表示对象的简单的后进先出集合。Stack接受空引用作为有效值,并且允许重复值。照例,在介绍该类的之前先来看一个简短的程序例子。
[align=left]//创建stack对象[/align]
[align=left]Stacks=newStack();[/align]
[align=left]//返回stack对象的包装[/align]
[align=left]Stacksyncs=Stack.Synchronized(s);[/align]
[align=left][/align]
[align=left]//压栈[/align]
[align=left]syncs.Push("a");[/align]
[align=left]syncs.Push("b");[/align]
[align=left]syncs.Push("c");[/align]
[align=left][/align]
[align=left]//锁定堆栈[/align]
[align=left]lock(syncs.SyncRoot)[/align]
[align=left]{[/align]
[align=left]//枚举[/align]
[align=left]IEnumeratori=syncs.GetEnumerator();[/align]
[align=left]while(i.MoveNext())[/align]
[align=left]{[/align]
[align=left]System.Console.WriteLine(i.Current);[/align]
[align=left]}[/align]
[align=left]}[/align]
[align=left][/align]
[align=left]//弹栈[/align]
[align=left]while(syncs.Count>0)[/align]
[align=left]{[/align]
[align=left]System.Console.WriteLine(syncs.Pop());[/align]
}
Stack类型的属性恐怕是所有集合类里面最简单的。只有三个,Count、IsSynchronized和SyncRoot。我们应该都很熟悉了。不许要详细叙述。而在Stack的为数不多的方法里只有Push、Pop和Peek三个方法是独特的。Push方法将对象压入堆栈的顶部。该方法只要求一个Object类型的参数。调用该方法会使Stack.Count加1。Pop方法把栈顶的元素弹出。该方法不要求参数,只有一个object类型的返回值。调用此方法过后,Stack.Count值减1。最后,Peek方法可以返回堆栈顶部的对象,但是并不把该对象移除。也就是说,调用此方法过后Stack.Count值不变。当堆栈为空时调用Pop、Peek方法会引发InvalidOperationException异常。
Queue类
队列是最后一个要为大家介绍的常用集合类型。关于什么是队列我实在不想多说了,我想大家都知道。而Queue类实现的正式这样一种对象的先进先出集合。这个类型实在是平淡无奇。我只介绍三个方法,分别是,Dequeue、Enqueue、Peek方法。Enqueue是入队操作,该方法将对象添加到队列尾部。唯一的参数要求传递一个object类型的对象。Dequeue是出队操作,该方法移除并返回队头对象。Peek方法返回队头对象,但是不将其移除。如果队列为空,那么调用Peek或Dequeue方法会引发InvalidOperationException异常。
创建类型严格的集合
在命名空间System.Collections当中有一个类型是专门提供给开发者用来建立自己的类型严格的集合的。这个类就是CollectionBase类。该类为强类型集合提供抽象基类。System.Collections命名空间提供此基类使开发者能够非常容易的创建自定义集合类型。开发者应该继承此类并加以扩充,而不建议创建自己的基类。CollectionBase类只有一个公共属性Count。该属性获取包含在CollectionBase实例中的元素个数。另外还有两个受保护属性提供给其子类。其中InnerList属性用于获取一个ArrayList,它包含CollectionBase实例中元素的列表。另一个List属性,用来获取一个IList,它同样包含CollectionBase实例中元素的列表。下面这段程序从CollectionBase类派生出名为Int16Collection的集合类型。
[align=left]staticvoidMain(string[]args)[/align]
[align=left]{[/align]
[align=left]Int16Collectioni=newInt16Collection();[/align]
[align=left][/align]
[align=left]i.Add(16);[/align]
[align=left]i.Add(28);[/align]
[align=left]i.Add(256);[/align]
[align=left][/align]
[align=left]IEnumeratorie=i.GetEnumerator();[/align]
[align=left]while(ie.MoveNext())[/align]
[align=left]{[/align]
[align=left]System.Console.WriteLine(ie.Current);[/align]
[align=left]}[/align]
[align=left][/align]
[align=left]System.Console.WriteLine(i.IndexOf(256));[/align]
[align=left][/align]
[align=left]System.Console.Read();[/align]
[align=left]}[/align]
[align=left]}[/align]
[align=left][/align]
[align=left]publicclassInt16Collection:CollectionBase[/align]
[align=left]{[/align]
[align=left]publicInt16Collection()[/align]
[align=left]{[/align]
[align=left]}[/align]
[align=left][/align]
[align=left]///<summary>[/align]
[align=left]///Int16Collection集合元素[/align]
[align=left]///</summary>[/align]
[align=left]publicInt16this[intindex][/align]
[align=left]{[/align]
[align=left]get[/align]
[align=left]{[/align]
[align=left]return(Int16)this.List[index];[/align]
[align=left]}[/align]
[align=left]set[/align]
[align=left]{[/align]
[align=left]this.List[index]=value;[/align]
[align=left]}[/align]
[align=left]}[/align]
[align=left][/align]
[align=left]publicintAdd(Int16value)[/align]
[align=left]{[/align]
[align=left]returnthis.List.Add(value);[/align]
[align=left]}[/align]
[align=left][/align]
[align=left]publicintIndexOf(Int16value)[/align]
[align=left]{[/align]
[align=left]returnthis.List.IndexOf(value);[/align]
[align=left]}[/align]
[align=left][/align]
[align=left]publicvoidInsert(intindex,Int16value)[/align]
[align=left]{[/align]
[align=left]this.List.Insert(index,value);[/align]
[align=left]}[/align]
[align=left][/align]
[align=left]publicvoidRemove(Int16value)[/align]
[align=left]{[/align]
[align=left]this.List.Remove(value);[/align]
[align=left]}[/align]
}
-----------
SortedList类类似于Hashtable和ArrayList间的混合。
与Hashtable一样,SortedList也基于IDictionary接口;因此,SortedList的每一元素都是键和值对。SortedList提供只返回键列表或只返回值列表的方法。
与ArrayList一样,SortedList是元素序列。它被索引,并且根据特定的比较器被排序。
SortedList在所有Collections类中是唯一的,在其中每一元素都可通过三种方式访问:使用键、值或索引。
如果您想要一个保存键/值对的集合并且还需要索引列表的灵活性,请使用SortedList。
相关文章推荐
- C# list存储的数据格式以及默认初始化空间,内存回收分析
- 深入.NET平台和C#编程_使用集合组织相关数据
- C#使用集合组织数据(HashTable、ArrayList、List<T>,Dictionary<K,V>
- C# 数据类型的引用类型、值类型内存存储方式以及区别; 函数参数传递的引用传递(址传递)、值传递区别
- S2 深入.NET和C#编程 三:使用集合组织相关数据
- C#中存储数据的集合:数组、集合、泛型、字典
- 大端存储 小端存储 (终极版本Byte Endian是指字节在内存中的组织,所以也称它为Byte Ordering,或Byte Order。 对于数据中跨越多个字节的对象, 我们必须为它建)
- C# list存储的数据格式以及默认初始化空间,内存回收分析
- 在jdbc下利用Oracle的存储过程取得比较复杂的数据集合
- redis这些内存消耗数据怎么看呢,主要看哪个说明内存比较大了?
- c#使用集合组织相关数据
- C#与C++数据类型比较及结构体转换(搜集整理二)
- (转载)对内存数据存放形式的很好的说明
- C# 泛型集合List和非泛型集合ArrayList的性能比较
- 数据在内存中存储方式学习
- C# 表复制和数据行的复制说明(Clone、ImportRow 、Copy )
- 在C#中用最简洁有效的代码执行存储过程并返回数据
- java中的各种数据类型在内存中存储的方式
- 【C/C++】【VS开发】结构体存储空间数据对齐说明
- (C#)两个DataTime类型数据比较大小