9.9.9 通用类(4)
for each循环迭代链表中的所有项,并在sum中累计总和。
Count属性返回链表中项的数量,Head和Tail属性返回第一项和最后一项的数值。First和Last属性分别与Head和Tail相同。
5. 存储键/值对的通用词典Dictionary<TKey, TValue>
通用的Dictionary<>集合类要求提供两个类型实参,第一个是键的类型,第二个是与键相关的值的类型。当需要存储的是对象对,而其中一个对象又是访问另一个对象的键时,词典就特别有用。姓名和电话号码是我们希望用词典来存储的键/值对的例子,因为我们通常希望使用姓名作为键来检索电话号码。假设我们已经定义了Name和PhoneNumber类来分别封装姓名和电话号码,那么可以用下面的语句来定义存储姓名/号码对的词典:
- Dictionary<Name^, PhoneNumber^>^ phonebook = gcnew Dictionary<Name^,
- PhoneNumber^>;
两个类型实参是Name^和PhoneNumber^,因此键是姓名的句柄,值是电话号码的句柄。
我们可以像下面这样在phonebook词典中添加新的记录项:
- Name^ name = gcnew Name("Jim", "Jones");
- PhoneNumber^ number = gcnew PhoneNumber(914, 316, 2233);
- phonebook->Add(name, number); // Add name/number pair to dictionary
如果想检索词典中的记录项,则可以使用默认的索引属性。例如:
- try
- {
- PhoneNumber^ theNumber = phonebook[name];
- }
- catch(KeyNotFoundFoundException^ knfe)
- {
- Console::WriteLine(knfe);
- }
我们提供键作为默认索引属性的索引值-- 本例中键是某个Name对象的句柄。如果该键存在,则返回对应的值。如果在集合中没有找到该键,则抛出一个KeyNotFoundException类型的异常。因此,访问键值时只要使用的键有可能不存在,我们就应该将代码放入try代码块内。
Dictionary<>对象有一个Keys属性,该属性返回包含词典中所有键的集合。还有一个Values属性,它返回包含词典中所有值的集合。Count属性返回词典中键/值对的数量。
下面在可运行的示例中实际使用一下通用集合类。
试一试:使用通用集合类
该示例练习使用前面介绍的3个集合类:
- // Ex9_22.cpp : main project file.
- // Using generic collection classes
-
- #include "stdafx.h"
-
- using namespace System;
- using namespace System::Collections::Generic; // For generic collections
-
- // Class encapsulating a name
- ref class Name
- {
- public:
- Name(String^ name1, String^ name2) : First(name1),Second(name2){}
- virtual String^ ToString() override{ return First + L" " + Second;}
- private:
- String^ First;
- String^ Second;
- };
-
- // Class encapsulating a phone number
- ref class PhoneNumber
- {
- public:
- PhoneNumber(int area, int local, int number):
- Area(area),Local(local), Number(number){}
- virtual String^ ToString() override
- { return Area + L" " + Local + L" " + Number; }
-
- private:
- int Area;
- int Local;
- int Number;
-
- };
-
- int main(array<System::String ^> ^args)
- {
- // Using List<T>
- Console::WriteLine(L"Creating a List<T> of integers:");
- List<int>^ numbers = gcnew List<int>;
- for(int i = 0 ; i<1000 ; i++)
- numbers->Add(2*i+1);
-
- // Sum the contents of the list
- int sum = 0;
- for(int i = 0 ; i<numbers->Count ; i++)
- sum += numbers[i];
- Console::WriteLine(L"Total = {0}", sum);
-
- // Using LinkedList<T>
- Console::WriteLine(L"\nCreating a LinkedList<T> of double values:");
- LinkedList<double>^ values = gcnew LinkedList<double>;
- for(int i = 0 ; i<1000 ; i++)
- values->AddTail(2.5*i);
-
- double sumd = 0.0;
- for each(double v in values)
- sumd += v;
-
- Console::WriteLine(L"Total = {0}", sumd);
-
- LinkedListNode<double>^ node = values->Find(20.0);
-
// Find node containing 20.0 - values->AddBefore(node, 19.9);
- values->AddAfter(values->Find(30.0), 30.1);
-
- // Sum the contents of the linked list again
- sumd = 0.0;
- for each(double v in values)
- sumd += v;
-
- Console::WriteLine(L"Total after adding values = {0}", sumd);
-
- // Using Dictionary<K,V>
- Console::WriteLine(L"\nCreating a Dictionary<K,V> of
name/number pairs:"); - Dictionary<Name^, PhoneNumber^>^ phonebook =
- gcnew Dictionary
<Name^, PhoneNumber^>; -
- // Add name/number pairs to dictionary
- Name^ name = gcnew Name("Jim", "Jones");
- PhoneNumber^ number = gcnew PhoneNumber(914, 316, 2233);
- phonebook->Add(name, number);
- phonebook->Add(gcnew Name("Fred","Fong"), gcnew PhoneNumber
- (123,234,3456));
- phonebook->Add(gcnew Name("Janet","Smith"), gcnew PhoneNumber
- (515,224,6864));
-
- // List all numbers
- Console::WriteLine(L"List all the numbers:");
- for each(PhoneNumber^ number in phonebook->Values)
- Console::WriteLine(number);
-
- // List names and numbers
- Console::WriteLine(L"Access the keys to list all name/number pairs:");
- for each(Name^ name in phonebook->Keys)
- Console::WriteLine(L"{0} : {1}", name, phonebook[name]);
-
- return 0;
- }
该示例的输出应该如下所示:
- Creating a List<T> of integers:
- Total = 1000000
- Creating a LinkedList<T> of double values:
- Total = 1248750
- Total after adding values = 1248800
-
- Creating a Dictionary<K,V> of name/number pairs:
- List all the numbers:
- 914 316 2233
- 123 234 3456
- 515 224 6864
- Access the keys to list all name/number pairs:
- Jim Jones : 914 316 2233
- Fred Fong : 123 234 3456
- Janet Smith : 515 224 6864
示例说明
注意指定System::Collections::Generic命名空间的那条using namespace指令。如果希望使用通用集合类时不必指定全限定的类名,那么该using指令就是必需的。
main()中的第一块代码使用了List<>集合,其中的代码与前面相同。该代码块创建了一个在列表中存储整数的类,然后向表中存入1000个数值。求列表内容总和的循环使用了默认的索引属性来检索数值,该循环还可以写成for each循环。不要忘记,默认索引属性只能访问列表中已有的项。我们可以使用默认索引属性修改现有项的值,但不能以这种方式添加新项。我们可以使用Add()函数将新项添加到表尾,也可以使用Insert()函数将新项添加到给定的索引位置。
main()中的下一块代码演示了使用LinkedList<>集合排序double类型数值的方法。我们在for循环中使用AddTail()函数,将double类型的数值添加到链表的尾部。使用AddLast()函数同样能够完成相同的任务。我们在for each循环中检索数值,并求出它们的总和。注意,链表中没有可用于访问表项的默认索引属性。这部分代码还演示了使用Find()、AddBefore()和AddAfter()函数,将新元素添加到链表中特定位置的方法。
main()中的最后一块代码演示了使用Dictionary<>集合,以姓名作为键存储电话号码的方法。Name和Phone number类重写了继承的ToString()函数,从而使Console::WriteLine()函数能够输出适当的这两种类对象的表示方法。有3个姓名/号码对被添加到phonebook词典中。然后使用for each循环,迭代phonebook的Values属性返回的集合对象所包含的所有值,从而输出词典中的所有电话号码。最后一个循环迭代Keys属性返回的集合所包含的所有姓名,并使用phonebook的默认索引属性访问电话号码。这里不需要try代码块,因为我们确信Keys集合中的所有键都在该词典中存在。如果不然,则Dictionary<>通用类的实现肯定存在严重问题!