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

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

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

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

// ---------- Main function ---------------------------------------

void main()

{

List<StringEx^>^ alist = gcnew List<StringEx^>();

alist->Add(gcnew StringEx("One")); alist->Add(gcnew StringEx("-")); alist[1] = gcnew StringEx("Three");

alist->Insert(1, gcnew StringEx("Two"));

List<StringEx^>^ morenums = gcnew List<StringEx^>(); morenums->Add(gcnew StringEx("Four")); morenums->Add(gcnew StringEx("Five"));

alist->AddRange(morenums);

//

alist[0] = "Six";

//

Compile time error not a StringEx

//

alist->Add("Six");

//

Compile time error not a StringEx

 

Console::WriteLine("***

The List<StringEx^> ***");

 

for (int i = 0; i

< alist->Count; i++)

 

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

 

// Find all words

in list that contain an 'e'

 

List<StringEx^>^ With_e

=

alist->FindAll(gcnew Predicate<StringEx^>(StringEx::With_e_Predicate));

Console::WriteLine("\n\n*** The List<StringEx^> containing an 'e' ***");

for each(StringEx^ str in With_e) Console::WriteLine("{0} ", str);

// Surround all elements with stars

alist->ForEach(gcnew Action<StringEx^>(StringEx::SurroundInStars));

Console::WriteLine("\n\n*** The List<StringEx^> surrounded by stars ***");

for each(StringEx^ str in alist) Console::WriteLine("{0} ", str);

Console::WriteLine("\n");

}

Figure 7-10 shows the results of the ListGeneric.exe program.

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

269

Figure 7-10. Results of ListGeneric.exe

LinkedList<T>

A linked list is probably one of the simplest types of collections available, second only to an array. Linked lists store arbitrarily located data in such a way as to make the data sequentially accessible. Specifically, the programmer writes a struct or class containing a handle pointing to the next (and, for a doubly linked list, to the previous) struct or class in the sequence.

A linked list has some advantages even over the array; the most notable advantage being that you can quickly insert and delete items in the sorted linked list. When you insert and delete items in a sorted array, you need to either make room for the new items or fill the hole left by deleting an item. These operations both require all elements after the insertion point to be copied up to the next element in the array in the case of insertion, or down in the case of a delete. The biggest disadvantage to a linked list is that you cannot immediately locate any particular element. Instead, you must traverse the list until you reach the element.

Now, with .NET version 2.0, you have a linked list built into the framework so you don’t have to write your own. You might argue that there are plenty of other, more powerful collections available. However, I think there is something to be said for the simplicity of the linked list and its lack of overhead requirements as compared to the other collection types.

The LinkedList<T> has two public constructors. The first is the default constructor, which takes no parameters and creates an empty link list:

LinkedList<datatype>^ list = gcnew LinkedList<datatype>();

The second takes an object implementing the IEnumerable<T> interface as a parameter. This allows the linked list to start with some existing data:

270

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

LinkedList<datatype>^ list =

gcnew LinkedList<datatype>((IEnumerable<datatype>^)existingList);

By the way, the array supports the IEnumerable<T> interface, so you can use it to initialize a

LinkedList<T>:

array<String^>^ arrList = gcnew array<String^> {"One", "Two", "Three"};

LinkedList<String^>^ list =

gcnew LinkedList<String^>((IEnumerable<String^>^)arrList);

The LinkedList<T> is very simple and limited. It is designed to be fast and have little overhead. If you want more features, then you have many more feature-rich collections from which to choose. The features you will most likely use are as follows:

The LinkedListNode<T> properties to the Head and Tail of the list

The Find() and FindList() methods, which return a LinkedListNode<T> to the first or last matching node in the list

The methods to add a new node to the list at the head (AddHead()), tail (AddTail()), before another node (AddBefore()), or after another node (AddAfter())

The methods to remove from the list at the head (RemoveHead()), tail (RemoveTail()), a specific node (Remove()), or all nodes (Clear())

To reference specific nodes within or to navigate through your LinkedList<T>, you need to use the LinkedListNode<T> class. (You can also use the for each statement to walk (only) forward through the

LinkedList<T> as well.

Navigation using LinkedListNode<T> is rather easy. All you do is get the next or previous node from LinkedList<T> via the handle property, pointing to the Next and Previous node on the accessed LinkedListNode<T> object. You know you have reached the beginning or end of the linked list when the Next or Previous property on the LinkedListNode<T> is a nullptr. The Value property contains the actual data being stored by the linked list.

Listing 7-10 shows a plethora of properties and methods of the List<T> and LinkedListNode<T> in action.

Listing 7-10. Working with Linked Lists

using namespace System;

using namespace System::Collections::Generic;

int main()

{

array<String^>^ arrList = gcnew array<String^> {"Two", "Three", "Four"};

LinkedList<String^>^ list =

gcnew LinkedList<String^>((IEnumerable<String^>^)arrList);

list->AddTail("Six"); list->AddHead("Zero"); list->AddAfter(list->Head, "One"); list->AddBefore(list->Tail, "5");

Console::WriteLine("Write with error");

LinkedListNode<String^>^ current = list->Tail;

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

271

while (current != nullptr)

{

Console::WriteLine(current->Value); current = current->Previous;

}

Console::WriteLine("\nNumber of elements = {0}", list->Count);

LinkedListNode<String^>^ node = list->Find("5");

list->AddBefore(node, "Five"); list->Remove(node);

list->RemoveHead();

Console::WriteLine("\nWrite with corrections"); for each (String^ str in list)

Console::WriteLine(str);

Console::WriteLine("\nNumber of elements = {0}\n", list->Count);

// list->Add(4);

// Compile time error as type is not a String^

}

 

Figure 7-11 shows the results of the LinkListGeneric.exe program.

Figure 7-11. Results of LinkedListGeneric.exe

Queue<T> and Stack<T>

Other than the syntax of the constructors, there is really no difference between the generic and standard versions. In fact, they contain nearly exactly the same methods and properties. A noticeable exception is the thread-safe properties and methods, which the generic collections lack.

272

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

Since there is nothing really new, I’ll just provide Listing 7-11, which is a conversion of the earlier standard Queue and Stack example to Queue<T> and Stack<T>. Notice that the only two lines that changed from QueueStack.cpp are

Queue<String^>^ que = gcnew Queue<String^>();

Stack<String^>^ stk = gcnew Stack<String^>();

Listing 7-11. Working with Queue<T>s and Stack<T>s

#using <system.dll>

using namespace System;

using namespace System::Collections::Generic;

void main()

{

Queue<String^>^ que = gcnew Queue<String^>(); Stack<String^>^ stk = gcnew Stack<String^>();

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]);

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-12 shows the results of the QueueStackGeneric.exe program.

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

273

Figure 7-12. Results of QueueStackGeneric.exe

Dictionary<K,V>, SortedDictionary<K,V>

The Dictionary<K,V> and SortedDictionary<K,V> are extremely handy key/value pair collections. With the addition of generics, you are now provided an elegant way to control your key/value pair type storage. Each allows you to define the data types of both the key and the value, and then ensure that those data types are the only ones used when implementing the collection.

The Dictionary<K,V> and SortedDictionary<K,V> are very similar in many respects. Obviously, as the collection names suggests, their biggest difference is that the SortedDictionary<K,V> is sorted. (Why do I want to write “Duh!” here?) You also have greater control over the elements when working with the SortedDictionary<K,V>, mainly because it is sorted.

Both dictionary collections have six constructors. The first three are pretty standard: default with no parameters, a parameter of capacity, and a parameter of IDictionary for preloading.

Dictionary<K,V>^ dict1 = gcnew Dictionary<K,V>();

SortedDictionary<K,V>^ dict2 = gcnew SortedDictionary<K,V>();

Dictionary<K,V>^ dict3 = gcnew SortedDictionary<K,V>(100);

SortedDictionary<K,V>^ dict4 = gcnew SortedDictionary<K,V>(100);

Dictionary<K,V>^ dict5 = gcnew SortedDictionary<K,V>(inDictionary);

SortedDictionary<K,V>^ dict6 = gcnew SortedDictionary<K,V>(inDictionary);

One thing that you might note is that since both the Dictionary<K,V> and SortedDictionary<K,V> inherit from the IDictionary interface, you can interchangeably load from either dictionary type.

One requirement of both these dictionary types is that the key’s data type needs to implement IComparable<K> or System::IComparable. If it doesn’t, then you need to use one of the three remaining constructors that take the additional parameter of IComparer<K>, thus adding the ability to compare the keys.

One cool feature is that you can implement your own version of IComparer<K> for the dictionary. This allows, in the case of SortedDictionary<K,V>, a way of sorting the elements as you wish.

You can load either of these dictionaries using either the Add() method or the default index property. The default index property takes as its index the key of the value.

dict->Add("Key1", "One"); dict["Key2"] = "Two";

274

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

All keys for either type of dictionary must be unique. If you try to repeat a key using the Add() method, the dictionary is going to throw the exception

System.ArgumentException: An item with the same key has already been added.

On the other hand, if you repeat a key using the default index property, the value is just replaced:

dict->Add("Key3",

"3");

 

 

dict["Key3"] = "Three";

//

replaces value

dict->Add("Key3",

"3");

//

throws exception

To access the value for a key, simply call the default index property with the index of the key:

String^ value = dict["Key3"];

Both dictionaries contain two properties to access their keys and values. These properties are implemented for the Dictionary<K,V> class using the classes Dictionary<K,V>::KeyCollection and Dictionary<K,V>::ValueCollection, and for the SortedDictionary<K,V> class using the classes SortedDictionary<K,V>::KeyCollection and SortedDictionary<K,V>::ValueCollection. From these classes, you grab an enumerator to the keys and values with the GetEnumerator() method:

Dictionary<K,V>::KeyCollection::Enumerator ^k = dict->Keys->GetEnumerator(); Dictionary<K,V>::ValueCollection::Enumerator ^v =dict->Values->GetEnumerator();

while ( k->MoveNext() && v->MoveNext())

{

Console::WriteLine("Key = [{0}]\tValue = [{1}]", k->Current, v->Current);

}

and

SortedDictionary<K,V>::KeyCollection::Enumerator ^k = dict->Keys->GetEnumerator();

SortedDictionary<K,V>::ValueCollection::Enumerator ^v = dict->Values->GetEnumerator();

while ( k->MoveNext() && v->MoveNext())

{

Console::WriteLine("Key = [{0}]\tValue = [{1}]", k->Current, v->Current);

}

Both dictionary types allow you to remove key/value pairs from the collection using the Remove() method, which takes as a parameter the key.

Okay, here is one last note before moving on to an example. A for each statement requires, as the first part of the statement, the type of each element in the collection. Since each element of the dictionaries is a key/value pair, the element type is not the type of the key or the type of the value. Instead, the element type is KeyValuePair<K,V>. Therefore, to use the for each statement to iterate through the collection, you need to code something similar to this:

for each (KeyValuePair<K,T> pair in dictionary)

{

Console::WriteLine("Key = [{0}]\tValue = [{1}]", pair->Key, pair->Value);

}

Listing 7-12 shows the Dictionary<K,V> and SortedDictionary<K,V> in action.

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

275

Listing 7-12. Working with Generic Dictionaries

#using <system.dll>

using namespace System;

using namespace System::Collections::Generic;

// Make the dictionary sort in reverse ref class Reverse : public IComparer<int>

{

public:

virtual int Compare(int x, int y) { return y - x; } virtual bool Equals(int x, int y) { return x == y; }

virtual int GetHashCode(int obj) { return obj.GetHashCode(); }

};

Dictionary<int,String^>^ DictionaryExample()

{

Dictionary<int,String^>^ dict = gcnew Dictionary<int,String^>();

dict->Add(1, "One"); dict->Add(6, "Six"); dict->Add(5, "Five");

dict->Add(3, "3");

//dict->Add(3, "3"); // throws an exception dict[3] = "Three";

dict[7] = "Seven";

String^ t = dict[3]; Console::WriteLine("dict[3] = {0}\n", t);

for each (KeyValuePair<int,String^>^ pair in dict)

{

Console::WriteLine("Key = [{0}]\tValue = [{1}]", pair->Key, pair->Value);

}

Console::WriteLine("\nDictionary contains 6? [{0}]", dict->ContainsKey(6));

dict->Remove(6);

Console::WriteLine("\nDictionary had 6 removed? [{0}]\n", !dict->ContainsKey(6));

Dictionary<int,String^>::KeyCollection::Enumerator ^key = dict->Keys->GetEnumerator();

Dictionary<int,String^>::ValueCollection::Enumerator ^value = dict->Values->GetEnumerator();

276

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

while ( key->MoveNext() && value->MoveNext())

{

Console::WriteLine("Key = [{0}]\tValue = [{1}]", key->Current, value->Current);

}

return dict;

}

void SortedDictionaryExample(Dictionary<int,String^>^ inDict)

{

SortedDictionary<int,String^>^ dict =

gcnew SortedDictionary<int,String^>(inDict, gcnew Reverse());

dict->Add(6, "Six");

String^ t = dict[3];

Console::WriteLine("dict[3] = {0}\n", t);

Console::WriteLine("Sorted Values:"); for each (String ^s in dict->Values)

Console::WriteLine("\t{0}",s);

Console::WriteLine();

for each (KeyValuePair<int,String^>^ pair in dict)

{

Console::WriteLine("Key = [{0}]\tValue = [{1}]", pair->Key, pair->Value);

}

Console::WriteLine("\nSortedDictionary contains 'Six'? [{0}]", dict->ContainsValue("Six"));

dict->Remove(6);

Console::WriteLine("\nSortedDictionary had 'Six' removed? [{0}]\n", !dict->ContainsValue("Six"));

SortedDictionary<int,String^>::KeyCollection::Enumerator ^key = dict->Keys->GetEnumerator();

SortedDictionary<int,String^>::ValueCollection::Enumerator ^value = dict->Values->GetEnumerator();

while ( key->MoveNext() && value->MoveNext())

{

Console::WriteLine("Key = [{0}]\tValue = [{1}]", key->Current, value->Current);

}

}

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

277

void main()

{

Console::WriteLine("Dictionary\n----------"); Dictionary<int,String^>^ dict = DictionaryExample();

Console::WriteLine();

Console::WriteLine("\nReverse SortedDictionary\n----------------");

SortedDictionaryExample(dict);

Console::WriteLine();

}

Figure 7-13 shows the results of the DictionaryGeneric.exe program.

Figure 7-13. Results of DictionaryGeneric.exe