All collections we examined so far were designed to store objects because they should accommodate all data types, built-in or custom. In most practical situations, however, we want to make sure that collections store elements of the same type, just like arrays that were declared with a specific type. Why not be able to declare collections that can store only variables of a specific type? The obvious advantage of such a collection is performance because we wouldn’t have to convert variables of the Object type to and from other more-specific types. This can have a substantial performance penalty. You can still process the ArrayList’s items without casting them to their corresponding type, but then you give up the benefits of type-safe programming. If your code calls a member that’s not supported by the specific object, the compiler won’t catch the error and an exception will be thrown when the statement is executed.
Let’s say you created the Rects ArrayList and you plan to store Rectangle objects in it. There’s no mechanism to prevent you from storing a Color object in this ArrayList. If the Color object was stored in the collection by mistake, it’s possible (if not likely) that you would attempt to treat it later as a Rectangle object. An attempt to request the Width property of a Color object will throw an exception. The compiler can’t catch this error at design time because collections are not typed.
If we can turn a collection into a typed collection, we’ll be able to use its items without casting them to a different type, and the collection itself would reject items of any other type. This is what a generic collection does: It is a typed collection that allows you to specify the type of objects you’ll store to the collection when you declare it. The generic collections are implemented in the System.Collection.Generic namespace; they’re equivalent to the regular collections but have different names.
Let’s consider a collection for storing employees. Each employee is defined with the following class:
Public Class Employee
Public ID As Long
Public Title As String
Public LastName As String
Public FirstName As String
Public SSN As String
Public HiredOn As Date
Public ReportsTo As Integer
One of the generic collections of the Framework is the List collection, which is similar to the ArrayList. The List collection provides the same functionality as the ArrayList collection, but it stores objects of the same type only: the type specified in the collection’s declaration. The following statement declares a List collection for storing Employee objects:
Dim LEmployees As New List(Of Employee)
The Of keyword in the constructor of the collection is followed by a data type. The LEmployees collection won’t accept elements of any other type, other than the Employee type. The List collection is also strongly typed, and you can use expressions such as the following:
This expression is early bound and as soon as you enter the dot following an element, the IntelliSense box will display the members of the Employee type.
A better example is that of a Dictionary collection. If you store the employees to a Dictionary and use the employee’s SSN as a key, you must declare the type of both the keys and the items you want to store to the Dictionary collection. In this case, the Of keyword will be followed by two types: one for the keys and another one for the elements (I’m assuming that you have imported the System.Collections.Generic namespace):
Dim Employees As New Dictionary(Of String, Employee)
This form of the declaration tells the compiler that the keys of the Dictionary will be strings, and the values will be Employee objects. To add the newEmployee object to the Employees Dictionary, use a few statements like the following:
Dim Emp As Employee
Emp.SSN = "334-19-0020"
After the collection has been populated, you can access the items of the collection by their keys with an expression like this one:
The employee’s name is given by the following expression:
Employees("334-19-0020").LastName & "," & _
You can do the same with a regular Dictionary (a nongeneric dictionary), but the editor won’t prevent you from requesting a nonexistent member such as FullName with a late-bound expression. To take advantage of type-safe programming, you must cast the items to their proper types. The reason for including generic collections in the Framework is performance, because the compiler generates code optimized for the type of objects you store in the collection. If you store the same collection of Employee objects to an ArrayList and a List collection, the ArrayList is at least five times slower than the List. The most important benefit of generic collections, however, is that they enable type-safe programming.
As far as the members of the generic collections are concerned, they’re the same as the members of the corresponding regular collections. The Dictionary generic collection, for example, is the typed version of the HashTable collection and it exposes the ContainsKey and ContainsValue methods, the Keys and Values properties to iterate through the items of the collection as described earlier in this chapter, the Add and Remove methods to manipulate its contents, and so on. The same is true for the List collection, which is the typed version of the ArrayList collection and it allows you to find specific items by using the FindIndex, FindIndexLast, and BinarySearch methods; sort its items via the Sort method; extract items via the CopyTo and ToArray methods; and so on. If the collection stores objects and not value types, you must pass to the Sort and BinarySearch methods the appropriate comparer.
The generic collections are implemented in the System.Collections.Generic namespace, and there are quite a few generic collections. However, not all collections implemented by the Framework have a generic counterpart. You can create generic Dictionary (equivalent to the HashTable untyped collection), SortedDictionary (it’s a typed Dictionary sorted by its keys), and List (it’s equivalent to the untyped ArrayList) collections. Note that the generic collections don’t have the same name as their regular counterparts, because if they did, you’d have to fully qualify their names in your code.
My recommendation is to use generic collections, which allow you to write strongly typed code. If you need a collection for storing elements of different types, use one of the regular collections. Eventually, all collections should be replaced by generic collections, and the usual collections will become special cases of generic collections. You can actually declare a generic collection for storing variables of the Object type.
In this chapter, we discussed how to use collections to store data in memory. To make the most of collections, however, you should be able to store them to disk files and read them back at a later session. This topic is covered a little later in the book, in Chapter “Serialization and XML”.