Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Pro Visual C++-CLI And The .NET 2.0 Platform (2006) [eng]-1

.pdf
Скачиваний:
70
Добавлен:
16.08.2013
Размер:
24.18 Mб
Скачать

248

C H A P T E R 7 C O L L E C T I O N S

alist->Reverse();

Console::WriteLine("*** The ArrayList ***");

for (int i = 0; i < alist->Count; i++)

{

Console::Write("{0} ", alist[i]);

}

Console::WriteLine("\n\nCapacity is: {0}", alist->Capacity.ToString());

alist->Capacity = 10;

Console::WriteLine("New capacity is: {0}", alist->Capacity.ToString()); Console::WriteLine("Count is: {0}", alist->Count.ToString()); alist->Sort();

int indx = alist->BinarySearch("Four");

Console::WriteLine("Four found at index: {0}", indx.ToString());

bool fnd = alist->Contains("One");

Console::WriteLine("ArrayList contains a 'One': {0}", fnd.ToString());

Console::WriteLine();

}

Figure 7-2 shows the results of the ArrayList.exe program.

Figure 7-2. Results of ArrayList.exe

BitArray

This is a neat little collection that stores an array containing only true and false values. Unlike the ArrayList, the length of the BitArray is fixed at creation. It can, on the other hand, be set to any length (memory permitting, of course).

There are several constructors for creating a BitArray. You can divide them into three different types. The first type simply sets a predetermined array length of bools to either true or false.

C H A P T E R 7 C O L L E C T I O N S

249

BitArray ^barray1 = gcnew BitArray( 8 );

//

Sets to false

BitArray ^barray2

= gcnew BitArray(

32, false

);

BitArray ^barray3

= gcnew BitArray(

256,

true

);

The second type takes an array of bools, unsigned chars, or ints and moves their bit values into the BitArray, where, in the case of unsigned chars and ints, bits of 1 are true and bits of 0 are false.

array<bool>^ bools = gcnew array<bool> { true, false, true, true, false }; BitArray ^barray1 = gcnew BitArray( bools );

array<unsigned char>^ chars = gcnew array<unsigned char> { 0x55, 0xAA }; BitArray ^barray2 = gcnew BitArray( chars );

array<int>^ ints = gcnew array<int> { 0x55555555, 0xAAAAAAAA }; BitArray ^barray3 = gcnew BitArray( ints );

The last constructor type takes one BitArray and copies it to another BitArray.

BitArray ^barray1 = gcnew BitArray( 8 );

BitArray ^barray2 = gcnew BitArray(barray1);

A convenient feature of BitArrays is that they can be treated as arrays of Booleans. The array is manipulated in the same way as an ArrayList—that is, using the default index property—but this time the array items are only bools.

barray1[1] = false; barray1[4] = true;

Console::WriteLine("Item[0]={0}", barray1[0]);

Console::WriteLine("Item[7]={0}", barray1[7]);

The functionality associated with BitArrays is obviously related to bit manipulation or, more specifically, AND, OR, XOR, and NOT. The basic idea around these bit manipulation methods is to take the original BitArray, and then take another and apply a bitwise operation on the two BitArrays.

BitArray ^barray1 = gcnew BitArray( 8 ); //...Manipulate bits for barray1 BitArray ^barray2 = gcnew BitArray( 8 ); //...Manipulate bits for barray2

barray2->And(barray1); barray2->Or(barray1); barray2->Xor(barray1);

The NOT method is a little different in that it only works on its own BitArray.

barray1->Not();

One last method that could come in handy is SetAll(). This method returns all the values in the BitArray back to either true or false depending on the value passed to it.

barray2->SetAll(true); barray2->SetAll(false);

Listing 7-2 shows the BitArray in action and demonstrates many of the functionalities described previously.

250 C H A P T E R 7 C O L L E C T I O N S

Listing 7-2. Working with BitArrays

using namespace System;

using namespace System::Collections;

void Print( BitArray ^barray, String ^desc)

{

Console::WriteLine(desc);

int i = 0;

for each( bool^ val in barray )

{

Console::Write("{0} ", val);

if (++i > 7)

{

Console::WriteLine(); i = 0;

}

}

Console::WriteLine();

}

void main()

{

BitArray ^barray1 = gcnew BitArray( 8, true ); Print(barray1, "BitArray( 8, true );");

barray1[1] = false; barray1[4] = false; barray1->Not();

Print(barray1, "Modified bit 1&4 then Not");

BitArray ^barray2 = gcnew BitArray( 8, true ); barray2->And(barray1);

Print(barray2, "And with BitArray( 8, true )");

barray2->SetAll(true); barray2->Or(barray1);

Print(barray2, "Or with BitArray( 8, true )");

barray2->SetAll(true); barray2->Xor(barray1);

Print(barray2, "Xor with BitArray( 8, true )");

array<unsigned char>^ chars = gcnew array<unsigned char> { 0x55, 0xAA }; BitArray ^barray3 = gcnew BitArray( chars );

Print(barray3, "BitArray(0x55, 0xAA);");

Console::WriteLine("Item[0]={0}", barray3[0]);

Console::WriteLine("Item[8]={0}", barray3[8]);

Console::WriteLine();

}

C H A P T E R 7 C O L L E C T I O N S

251

Figure 7-3 shows the results of the BitArray.exe program.

Figure 7-3. Results of BitArray.exe

Hashtable and SortedList

The Hashtable is a powerful method for storing data. The Hashtable works by storing its values in memory and then uses its key to later look up these values. What makes the Hashtable so powerful is that it doesn’t search through all the keys to find a match; instead, it takes the key and analyzes it to figure out the index to the key’s value. It then retrieves the value using this index.

The SortedList is a combination of a Hashtable and an Array. Depending on how you access the SortedList, it will respond like a Hashtable or an Array. For example, if you access the SortedList using the default index property, it works like a Hashtable. On the other hand, if you use the

GetByIndex() method, the SortedList works like an Array.

A SortedList can do everything that a Hashtable can do and more. To access the values out of a Hashtable, you use the key. With a SortedList, on the other hand, you can use the key or access the data in a sorted manner directly using an index, making retrieval very fast. The cost of this added functionality is that the SortedList is slower for deletes, updates, and inserts.

The reason the SortedList is slower is that both the keys and the values must be accessible in a sorted manner. This means that when data is added to or removed from the SortedList, the values may be inserted into or removed from the internal value array. This requires memory manipulation. For the Hashtable, the values do not normally require this manipulation. (I had to add the “normally” qualifier in the previous sentence, because as those of you who understand the internal workings of the Hashtable know, a Hashtable can have the same bad performance if multiple keys hash to the same bucket.)

Both the Hashtable and SortedList have numerous constructors, but in most cases, you will probably simply use the default constructor.

Hashtable ^hashtable = gcnew Hashtable();

SortedList ^sortedlist = gcnew SortedList();

On the other hand, all the other constructors provide parameters to help with the efficiency of the collection.

252

C H A P T E R 7 C O L L E C T I O N S

A major factor both the Hashtable and SortedList have in common is Capacity. If many entries are to be made into these collection types, then creating them with a sufficiently large capacity allows the entries to be inserted more efficiently than if you let them perform automatic rehashing as needed to grow the collections.

Hashtable ^hashtable = gcnew Hashtable(300);

SortedList ^sortedlist = gcnew SortedList(300);

A Hashtable constructor provides another parameter to further refine the collection’s efficiency: the load factor. The load factor is the ratio of the number of filled buckets to the total number of buckets available. A bucket is full when it points to or contains a data element. The load factor is a value between 0.1 and 1.0. A smaller load factor means faster lookup at the cost of increased memory consumption. Conversely, a larger load factor uses memory more efficiently at the cost of longer expected time per lookup. The default load factor of 1.0 generally provides the best balance between speed and size.

Hashtable ^hashtable = gcnew Hashtable(300, 0.75);

You use the Add() method to load these collections. Neither the Hashtable nor the SortedList have an insert method. If you think about it, an insert really doesn’t make sense, because the Hashtable analyzes the key and doesn’t care where the values are located, and the SortedList is sorted whenever the Add() method is invoked.

hashtable->Add(nullptr, "zero"); sortedlist->Add("A", "two");

Note Database programmers, take note that in the preceding example, null is a valid key.

Unloading individual elements in the Hashtable and SortedList requires the use of the Remove() method and the specific key. The SortedList also allows elements of the collection to be removed by index value using the RemoveAt() method. It is also possible to remove all the elements of the collections using the Clear() method.

hashtable->Remove(nullptr); hashtable->Clear(); sortedlist->Remove( "A" ); sortedlist->RemoveAt( 2 ); sortedlist->Clear();

Now that you can put key/value pairs into a Hashtable and a SortedList, you need to be able to get them out. Both of these collection types provide a plethora of methods to do just that. One of the easiest methods is to use the default index property. Be careful: this is not an array property like you have seen in the previous collection types. A default index property, if you recall from Chapter 3, takes an Object instead of an integer value type between the square brackets, which you normally associate with an array. In this case, the object you would use is the key of the value you wish to retrieve.

Console::WriteLine("key="A" value={1}", hash["A"]);

Console::WriteLine("key="A" value={1}", sort["A"]);

If you don’t know the keys or you simply want all the data and, in the case of a Hashtable, don’t care about the order, then you can enumerate through the collections. It’s possible to enumerate by key, by value, or by both key and value at the same time. To get the enumerator, you need to use the Keys property, the Values property, or the GetEnumerator() method.

C H A P T E R 7 C O L L E C T I O N S

253

IDictionaryEnumerator ^enum1 = hash->GetEnumerator();

IDictionaryEnumerator ^enum2 = sort->GetEnumerator();

IEnumerator ^keys1 = hash->Keys->GetEnumerator();

IEnumerator ^keys2 = sort->Keys->GetEnumerator();

IEnumerator ^vals1 = hash->Values->GetEnumerator();

IEnumerator ^vals2 = sort->Values->GetEnumerator();

Enumerating by both key and value at the same time is a little different from what you have seen so far. You need to use the IDictionaryEnumerator interface instead of IEnumerator. Also, to retrieve the key and value from the collection, you use the Key and Value properties and not the Current property (see Listing 7-3 for an example).

The code to enumerate keys and values on their own, though, is no different than any other collection.

If you are not sure, but you want a quick way to see if a Hashtable or SortedList contains a key or a value, you would use the ContainsKey() (or Contains()) method and the ContainsValue() method. Simply use the key or value you are searching for as a parameter. The methods will return true

or false.

bool b1 = hash->Contains("A"); bool b2 = sort->Contains("A"); bool b3 = hash->ContainsKey("Z"); bool b4 = sort->ContainsKey("Z");

bool b5 = hash->ContainsValue("cat"); bool b6 = sort->ContainsValue("cat");

Three methods specific to SortedList are based on indexes to values. Because a Hashtable doesn’t have an index to its values, these methods wouldn’t make sense, so they aren’t included. You can get a value by index or you can get the index of a key or a value.

Console::WriteLine("Index {0} contains: {1}", i, sort->GetByIndex(i));

Console::WriteLine("Index key 'B': {0}", sort->IndexOfKey(“B"));

Console::WriteLine("Index val 'cat': {0}", sort->IndexOfValue("cat"));

Listing 7-3 shows the Hashtable and SortedList in action and demonstrates the functionality described previously.

Listing 7-3. Working with Hashtables and SortedLists

using namespace System;

using namespace System::Collections;

void main()

{

Hashtable ^hash = gcnew Hashtable(); SortedList ^sort = gcnew SortedList();

array<String^>^ keys = gcnew array<String^> {"B", "A", "C", "D"}; array<String^>^ skeys = gcnew array<String^>{"A", "B", "C", "D"}; array<String^>^ values = gcnew array<String^> {"moose", "zebra",

"horse", "frog" };

for (int i = 0; i < keys->Length; i++)

{

hash->Add(keys[i], values[i]); sort->Add(keys[i], values[i]);

}

254

C H A P T E R 7 C O L L E C T I O N S

Console::WriteLine("Hashtable\tSortedList");

Console::WriteLine("By indexed property"); for (int i = 0; i < hash->Count; i++)

{

Console::WriteLine("{0} {1}\t\t{2} {3}", skeys[i], hash[skeys[i]], skeys[i], sort[skeys[i]]);

}

Console::WriteLine("\nBy index");

for (int i = 0; i < sort->Count; i++)

{

Console::WriteLine("N/A\t\t{0} {1}", i, sort->GetByIndex(i));

}

Console::WriteLine("\nBy enumerator"); IDictionaryEnumerator ^enum1 = hash->GetEnumerator(); IDictionaryEnumerator ^enum2 = sort->GetEnumerator(); while ( enum1->MoveNext() && enum2->MoveNext())

{

Console::Write("{0} {1}\t\t", enum1->Key, enum1->Value); Console::WriteLine("{0} {1}", enum2->Key, enum2->Value);

}

Console::WriteLine("\nEnumerate Key"); IEnumerator ^keys1 = hash->Keys->GetEnumerator(); IEnumerator ^keys2 = sort->Keys->GetEnumerator(); while ( keys1->MoveNext() && keys2->MoveNext())

{

Console::Write("{0}\t\t", keys1->Current); Console::WriteLine("{0}", keys2->Current);

}

Console::WriteLine("\nEnumerate Value"); IEnumerator ^vals1 = hash->Values->GetEnumerator(); IEnumerator ^vals2 = sort->Values->GetEnumerator(); while ( vals1->MoveNext() && vals2->MoveNext())

{

Console::Write("{0}\t\t", vals1->Current); Console::WriteLine("{0}", vals2->Current);

}

Console::WriteLine("\nContains a Key 'A' and 'Z'"); Console::WriteLine("{0}\t\t{1}", hash->Contains("A"),

sort->Contains("A")); Console::WriteLine("{0}\t\t{1}", hash->ContainsKey("Z"),

sort->ContainsKey("Z"));

Console::WriteLine("\nContains a Value 'frog' and 'cow'"); Console::WriteLine("{0}\t\t{1}", hash->ContainsValue("frog"),

sort->ContainsValue("frog")); Console::WriteLine("{0}\t\t{1}", hash->ContainsValue("cow"),

sort->ContainsValue("cow"));

C H A P T E R 7 C O L L E C T I O N S

255

Console::WriteLine("\n\t\t'B' key index: {0}", sort->IndexOfKey("B"));

Console::WriteLine("\t\t'frog' value index: {0}", sort->IndexOfValue("frog"));

}

Figure 7-4 shows the results of the HashSortList.exe program.

Figure 7-4. Results of HashSortList.exe

Queue and Stack

The Queue and Stack collections are simple but handy. If you have ever been to an amusement park and waited to get on a ride, then you should be very familiar with a queue. Basically, the order you go in is the order you come out. A Queue is often known as a first-in-first-out (FIFO) collection. The best real-world example that I know of a stack is a plate dispenser at an all-you-can-eat buffet. Here, the last plate placed in is the first one out. A Stack is often known as a last-in-first-out (LIFO) collection.

The Queue and Stack collections don’t provide a vast array of methods, as many of the other collections do. They do both contain the standard Count property, and the GetEnumerator() and

Contains() methods.

Even the constructors of a Queue and a Stack are quite simple. You can create them from another collection, specifying their initial size or taking the default size.

256

C H A P T E R 7 C O L L E C T I O N S

Queue ^que1 = gcnew Queue();

Stack ^stk1 = gcnew Stack();

Queue ^que2 = gcnew Queue(8);

Stack ^stk2 = gcnew Stack(8);

Queue ^que3 = gcnew Queue(stk1);

Stack ^stk3 = gcnew Stack(que1);

Both the Queue and Stack have one more method in common: the Peek() method. This method allows the program to see the next element that is going to come off the Queue or Stack but does not actually remove it.

Console::WriteLine( que->Peek() );

Console::WriteLine( stk->Peek() );

Both the Queue and Stack collections have the same process of placing elements on and off. However, they use different method names that more closely resemble the type of collection they are. To place an element onto a Queue, you use the Enqueue() method, and to take an element off the Queue, you use the Dequeue() method. (I know, neither of these method names is actually an English word, but hey, we’re programmers, not authors. Wait a minute—I am!)

que->Enqueue("First"); que->Dequeue();

To place an element onto a Stack, you use the Push() method, and to take it off, you use the Pop() method.

stk->Push("First"); stk->Pop();

There are occasions when you want to Dequeue or Pop all elements of the Queue or Stack. You can do this with the single method Clear().

Listing 7-4 shows the Queue and Stack in action and demonstrates the functionality described previously.

Listing 7-4. Working with Queues and Stacks

using namespace System;

using namespace System::Collections;

void main()

{

Queue ^que = gcnew Queue(); Stack ^stk = gcnew Stack();

array<String^>^ entry = gcnew array<String^> { "First", "Second", "Third", "Fourth"

};

Console::WriteLine("Queue\t\tStack");

Console::WriteLine("** ON **");

for (int i = 0; i < entry->Length; i++)

{

que->Enqueue(entry[i]); stk->Push(entry[i]);

C H A P T E R 7 C O L L E C T I O N S

257

Console::WriteLine("{0}\t\t{1}", entry[i], entry[i]);

}

Console::WriteLine("\n** OFF **");

while ((que->Count > 0) && (stk->Count > 0))

{

Console::WriteLine("{0}\t\t{1}", que->Dequeue(), stk->Pop());

}

que->Clear(); stk->Clear();

Console::WriteLine("\n");

}

Figure 7-5 shows the results of the QueueStack.exe program.

Figure 7-5. Results of QueueStack.exe

Specialized Collections

Now that you have covered all of the standard collections, you’ll take a look at a few of the more commonly used specialized collections provided by the .NET Framework class library. Unlike the standard set of collections that I discussed previously, these specialized collections require the referencing of the System.dll assembly and use the System::Collections::Specialized namespace.

#using <system.dll>

using System::Collections::Specialized;

ListDictionary

If you require quick access to a short list of elements, a ListDictionary might just be what you need. It has very little overhead. It is just a singular linked list, which makes it very fast for one-way access in the creation order, if you plan on restricting the number of data elements.