BitArraybool[] // Copy constructor from a specific array of booleans.BitArraybyte[] // Copy constructor from a specific array of bytes.BitArrayint[] // Copy constructor from a specific a
Trang 1void Remove(object value);
void RemoveAt(int index);
}
The property IsFixedSize returns true if a collection derived from IList has a fixed size.Otherwise, it returns false Similarly, the IsReadOnly property returns true if the col-lection is read-only Otherwise, it returns false The indexer this[int index] gets andsets an item at a specified index The methods Add, Clear, and Contains add an item tothe collection, remove all items from the collection, and determine whether the collectioncontains a specific value The method IndexOf simply returns the index of a specific item
in the collection whereas the method Insert places an item in the collection at a specifiedlocation Finally, the methods Remove and RemoveAt delete the first occurrence of a specificvalue and delete the item at a specified index, respectively
Constructors
Like all classes, instances of collections are created using constructors Concrete tions have several constructors that typically fall into one of the following categories:Without parameters (default), with a collection to be added, or with an initial capacity
collec-of items The constructors for the BitArray, ArrayList, Stack, and Queue collections aregiven below The constructors for Hashtable and SortedList follow in Section 8.1.3
BitArray(int n, bool v) // Constructor that initializes n bits,
// each to boolean value v
BitArray(BitArray) // Copy constructor from a specific BitArray
BitArray(bool[]) // Copy constructor from a specific array of booleans.BitArray(byte[]) // Copy constructor from a specific array of bytes.BitArray(int[]) // Copy constructor from a specific array of integers.ArrayList() // Default constructor with initial capacity 16
ArrayList(ICollection) // Copy constructor from a specific collection
ArrayList(int) // Constructor with a specific initial capacity
Stack() // Default constructor with initial capacity 10
Stack(ICollection) // Copy constructor from a specific collection
Stack(int) // Constructor with a specific initial capacity
Queue() // Default constructor with initial capacity 32
Queue(ICollection) // Copy constructor from a specific collection
Queue(int) // Constructor with a specific initial capacity
Queue(int, float) // Constructor with a specific initial capacity
// and growth factor
Aside from BitArray, the size of each collection is doubled when the collection reachesits current capacity In the case of Queue, the growth factor may be explicitly specified as
Trang 2a parameter A subset of the previous constructors is exercised in the following example.The methods, Push and Dequeue of the Stack and Queue collections, perform as expected
by adding an object to the top of a stack and by removing and returning an object fromthe front of a queue If the capacity of the Stack is reached when adding an object, thenthe size of the Stack is doubled On the other hand, if the Queue is empty when a Dequeueoperation is performed, then an InvalidOperationException is generated
using System;
using System.Collections;
namespace T {
public class TestBasicCollections {
public static void Print(string name, ICollection c) {
Console.Write("[{0,2} items] {1,2}: ", c.Count, name);
foreach (bool b in c)
Console.Write("{0} ", b ? "1" : "0");
Console.WriteLine();
}
public static void Main() {
byte[] bytes = { 0x55, 0x0F }; // two bytes (16 bits)
bool[] bools = { true, false, true };
BitArray b1 = new BitArray(8, true);
BitArray b2 = new BitArray(bytes);
BitArray b3 = new BitArray(bools);
ArrayList a = new ArrayList(bools);
Trang 3Notice that each list-type collection is passed from Main to the Print method via a localparameter c of the parent type ICollection Hence, when the foreach statement is exe-cuted, the GetEnumerator method of the passed collection is polymorphically invoked togenerate the following output:
using System;
using System.Collections;
namespace T {
public class TestBasicCollections {
public static void Main() {
ArrayList a = new ArrayList();
a.Add("A"); a.Add("B"); a.Add("C");
Console.WriteLine("Capacity: {0} items", a.Capacity);
Console.WriteLine("Count: {0} items", a.Count);
Console.WriteLine("IsFixedSize? {0}", a.IsFixedSize);
Console.WriteLine("IsReadOnly? {0}", a.IsReadOnly);
Console.WriteLine("IsSynchronized? {0}", a.IsSynchronized);
Console.WriteLine("a[0] = {0}", a[0]);
Console.WriteLine("a[0] = {0}", a[0] = "a");
Console.WriteLine("\"B\" found? = {0}", a.Contains("B"));
Console.WriteLine("\"B\" index = {0}", a.IndexOf("B"));
Trang 48.1.3 Using Dictionary-Type Collections
Dictionary-type collections, SortedList, Hashtable, and DictionaryBase, contain objectsthat are accessed, inserted, and deleted based on their key values Hence, the iteratorsand constructors for dictionary-type collections require the support of other interfacesincluding IDictionary The IDictionary interface, in particular, defines each entry in acollection as a key/value pair
Iterators
As stated in the previous section, all collections inherit from the IEnumerable interface,which is used to create and return an enumerator that iterates through a collection How-ever, in order to iterate through and to access the items of any IDictionary collection,the enumerator interface for a dictionary-type collection inherits from IEnumerator andincludes an additional three properties as shown here:
interface IDictionaryEnumerator : IEnumerator {
DictionaryEntry Entry {get;}
}
The property Entry returns the key/value pair of the current item in the collection Eachkey/value pair is represented by a DictionaryEntry structure that also includes separateproperties for Key and Value:
struct DictionaryEntry {
public DictionaryEntry(object key, object value) { }
public object Key {get; set;}
public object Value {get; set;}
}
Trang 5Hence, the Key and Value properties of IDictionaryEnumerator access the Key and Valueproperties of DictionaryEntry.
Like list-type collections, the GetEnumerator method, which creates and returns anenumerator for a given dictionary-type collection, may be invoked either explicitly orimplicitly Consider an instance of the Hashtable collection called htable Three namesare added to htable using its Add method Once the names, given as key/value pairs, havebeen added, the method GetEnumerator is explicitly invoked It returns a reference to theenumerator of htable and assigns it to e:
Hashtable htable = new Hashtable();
htable.Add("Brian", "Brian G Patrick");
htable.Add("Michel", "Michel de Champlain");
Hashtable htable = new Hashtable();
htable.Add("Brian", "Brian G Patrick");
htable.Add("Michel", "Michel de Champlain");
Trang 6the IComparer interface is implemented by the Comparer class, which makes a sensitive comparison between strings The CaseInsensitiveComparer class, on the otherhand, performs case-insensitive string comparisons In Section 7.3.1, a similar interfacecalled IComparable was introduced In this interface restated here, the method CompareTocompares the current object with its single parameter:
on key/value pairs
public interface IDictionary : ICollection, IEnumerable {
object this[object key] {get; set;}
ICollection Keys {get;}
ICollection Values {get;}
int Add(object value);
void Clear();
bool Contains(object value);
void Remove(object value);
IDictionaryEnumerator GetEnumerator();
}
The first two properties, IsFixedSize and IsReadOnly, as well as the methods Add, Clear,Contains, and Remove are the same as their corresponding members in IList The indexerthis[object key] gets and sets an item at a specified key (rather than index) The twoadditional properties, Keys and Values, return collections that contain the keys and values
of a collection derived from IDictionary Finally, the GetEnumerator method inheritedfrom IEnumerable is redefined to return a IDictionaryEnumerator instead of IEnumerator
Trang 7Many constructors are available for dictionary-like collections, especially for Hashtables.Unless otherwise specified by a given comparer, the objects within a SortedList col-lection are ordered according to an implementation of IComparable This implementa-tion defines how keys are compared Only a subset of these constructors is providedhere:
Hashtable(IDictionary) // From a specific dictionary
Hashtable(IDictionary, float) // From a specific dictionary and
// loading factor
Hashtable(IHashCodeProvider, IComparer)
// With a specific hash code provider// and comparer
SortedList(IComparer) // With a specific comparer
SortedList(IDictionary) // From a specific dictionary
SortedList(IComparer, int) // With a specific comparer and initial
// capacity
SortedList(IDictionary, IComparer)
// From a specific dictionary using comparer.The Hashtable collection represents buckets that contain items Each bucket is associatedwith a hash code based on the key value of an item Barring excessive collisions betweenitems with identical hash codes, a hash table is designed for faster and easier retrieval thanmost collections The SortedList collection, on the other hand, is a combination between
a Hashtable, where an item is accessed by its key via the indexer [ ], and an Array, where
an item is accessed by its index via the GetByIndex or SetByIndex methods The itemsare sorted on keys using either an implementation of IComparer or an implementation ofIComparable provided by the keys themselves The index sequence is based on the sortedorder, where the insertion and removal of items re-adjust the sequence automatically.Sorted lists, therefore, are generally less efficient than hash tables For both hash tablesand sorted lists, however, no duplicate keys are allowed
The following example demonstrates the functionality of both Hashtable andSortedList A class ReverseOrderComparer (lines 6–13) inherits and implements theIComparer interface to sort objects in reverse order In addition, the class HashCodeGen(lines 15–20) inherits and implements the IHashCodeProvider interface to return on line 18
a hash code that is calculated as the sum of the first and last characters of an object (oncecast to a string) and the length of the string itself
1 using System;
2 using System.Collections;
Trang 84 namespace T {
5 // To sort in reverse alphabetical order
6 public class ReverseOrderComparer : IComparer {
7 public int Compare(object x, object y) {
14 // To get a different hash code (first + last + length)
15 public class HashCodeGen : IHashCodeProvider {
16 public int GetHashCode(object o) {
22 public class TestDictionaries {
23 public static void Print(string name, IDictionary d) {
31 h1.Add("Michel", "Michel de Champlain");
32 h1.Add("Brian", "Brian G Patrick");
33 h1.Add("Helene", "Helene Lessard");
34
36 SortedList s2 = new SortedList(h1, new ReverseOrderComparer());
37 Hashtable h2 = new Hashtable(h1, new HashCodeGen(),
Trang 9as the second parameter Finally, a second instance h2 of Hashtable is created on line 37and implemented to use HashCodeGen and ReverseComparer The output is as follows:h1: Helene Michel Brian
s1: Brian Helene Michel
s2: Michel Helene Brian
h2: Brian Michel Helene
h2: Brian Michel Helene Be
h1: Helene Be Michel Brian
8.1.4 Using Iterator Blocks and yield Statements
Iterating through arrays and collections is neatly handled using the foreach loop, as shownhere on a simple array of integers:
int[] numbers = {1, 2, 3, 4, 5, 6, 7, 8, 9};
foreach (int n in numbers)
System.Console.WriteLine("{0}", n);
Any collection implicitly creates an iterator associated with a foreach statement as long
as two interfaces, IEnumerable and IEnumerator, are implemented The following ple shows a typical implementation of two classes that implement IEnumerable andIEnumerator, respectively The first class called IntCollection implements the singlemethod GetEnumerator declared in IEnumerable The second class called IntEnumeratorimplements the three members (Current, MoveNext, and Reset) declared in IEnumerator.The IntEnumerator class also encapsulates the internal state of a collection defined by itsreference, its index, and possibly other information
exam-1 using System.Collections;
2
3 public class IntCollection : IEnumerable {
4 public IntCollection(int[] numbers) {
7 public virtual IEnumerator GetEnumerator() {
Trang 1014 class IntEnumerator : IEnumerator {
15 public IntEnumerator(int[] list) {
18 public object Current {
21 public bool MoveNext() {
22 return ++index < list.Length;
24 public void Reset() {
29 }
30
31 class TestIntCollection {
32 public static void Main() {
33 IntCollection ic = new IntCollection(new int[]
3 public class IntCollection : IEnumerable {
4 public IntCollection(int[] numbers) {
7 public virtual IEnumerator GetEnumerator() {
8 for (int n = 0; n < numbers.Length; n++)
Trang 1111 //
12 private int[] numbers;
13 }
Any block statement that contains at least one yield statement is called an iterator block.
In the previous example, the block statement from lines 7 to 10 defines an iterator blockand produces an ordered sequence of values The identifier yield is not a keyword itself,but rather has a contextual meaning when immediately preceding a return or break key-word On one hand, the yield return generates the next iteration value, and on the otherhand, a yield break immediately ends an iteration
By using a yield return statement on line 9, the GetEnumerator method returns
an object reference each time the foreach statement calls the iterator Traversing thecollection is simply a matter of stepping through the collection using a loop in an iteratorblock and yielding the next object reference upon every iteration In another example,which follows, the iterator block iterates through an array list and returns even valuesuntil either the list is exhausted or a value greater than 9 is reached In the latter case, ayield break is invoked to end the iteration
public IEnumerator GetEnumerator() {
for (int n = 0; n < numbers.Length; n++) {
if (numbers[n] >= 10) yield break;
if (numbers[n]%2 == 0 ) yield return numbers[n];
}}
Many object-oriented core libraries provide data structures or collections, such asstacks, queues, linked lists, hash tables, and so on One of the main advantages of thesecollections is their heterogeneity Because the input and return parameters of collectionmethods are of root type object, collections may be composed of any data type Thefollowing is a typical example of a Queue class, where objects are inserted at the tail usingthe Enqueue method and retrieved from the head using the Dequeue method:
public class Queue {
public void Enqueue(object n) { }
public object Dequeue() { }
}
Trang 12These heterogeneous collections also have many disadvantages First, the flexibility ofadding and removing data of any type comes with an overhead Each time a value type ispassed to the Enqueue method and returned by the Dequeue method, the value is implicitlyboxed and explicitly unboxed as shown here:
Queue q = new Queue();
q.Enqueue(23); // The integer 23 is implicitly boxed
int i = (int)q.Dequeue(); // The object (23) must be cast back (unboxed)
// to an int
These implicit and explicit operations involve both memory allocations and runtime typechecks Although boxing/unboxing operations are not applicable with reference types, thedata retrieved must still be explicitly cast back to the proper type as shown here:
Queue q = new Queue();
q.Enqueue("C#"); // The string "C#" reference is passed
// (no boxing)
string s = (string)q.Dequeue(); // The object reference ("C#") must be
// cast back to a string (no unboxing)
In the previous example, performance is compromised, even though there are no ing/unboxing operations In other words, the runtime system must perform type checking
box-on the object that is cast back to ensure that the object retrieved is really of type string
as specified by the explicit cast In any case, there is always a risk that the user of a queuewill wrongly cast an object as shown here
Trang 13with a type parameter T as shown here:
public class Queue<T> {
public class BoundedQueue {
public BoundedQueue(int capacity) {
items = new int[capacity];
}
public void Enqueue(int item) { }
private int[] items;
private int head;
private int tail;
private int capacity;
}
The corresponding generic class for BoundedQueue parameterizes its item type as T:
public class BoundedQueue<T> {
public BoundedQueue(int capacity) {
items = new T[capacity];
private int head;
private int tail;
private int capacity;
}