The Visual Basic collection classes basically hold items and don’t provide a lot of extra functionality. Other classes described later in this chapter provide more features.
The following sections describe the simple collection classes in Visual Basic: ArrayList, StringCollection, and NameValueCollection. They also describe strongly typed collections that you can build to make code that uses these classes a bit easier to debug and maintain.
The ArrayList class is a resizable array. You can add and remove items from any position in the list and it resizes itself accordingly. The following table describes some of the class’s more useful properties and methods.
Property/Method | Purpose |
---|---|
Add | Adds an item at the end of the list. |
AddRange | Adds the items in an object implementing the ICollection interface to the end of the list. |
BinarySearch | Returns the index of an item in the list. The items must implement the IComparable interface, or you must provide the Sort method with an IComparer object. |
Capacity | Gets or sets the number of items that the list can hold. For example, if you know that you will need to add 1000 items to the list, you may get better performance by setting Capacity to 1000 before starting, rather than letting the object grow incrementally as you add the items. |
Clear | Removes all of the items from the list. The Capacity property remains unchanged, so the ArrayList keeps any space it has previously allocated to improve performance. |
Contains | Returns True if a specified item is in the list. |
CopyTo | Copies some or the entire list into a one-dimensional Array object. |
Count | The number of items currently in the list. This is always less than or equal to Capacity. |
GetRange | Returns an ArrayList containing the items in part of the list. |
IndexOf | Returns the zero-based index of the first occurrence of a specified item in the list. |
Insert | Adds an item at a particular position in the list. |
InsertRange | Adds the items in an object implementing the ICollection interface to a particular position in the list. |
Item | Returns the item at a particular position in the list. |
LastIndexOf | Returns the zero-based index of the last occurrence of a specified item in the list. |
Remove | Removes the first occurrence of a specified item from the list. |
RemoveAt | Removes the item at the specified position in the list. |
RemoveRange | Removes the items in the specified positions from the list. |
Reverse | Reverses the order of the items in the list. |
SetRange | Replaces the items in part of the list with new items taken from an ICollection object. |
Sort | Sorts the items in the list. The items must implement the IComparable interface, or you must provide the Sort method with an IComparer object. |
ToArray | Copies the list’s items into a one-dimensional array. |
TrimToSize | Reduces the list’s allocated space so that it is just big enough to hold its items. This sets Capacity = Count. |
The ToArray method can copy the items into an array of objects, an array of a specific type, or an Array object (holding objects). The following code demonstrates each of these. ToArray returns a generic array of Object even if the objects are some other data type such as strings, so the second method must use a DirectCast statement to convert the result into an array of strings, if Option Strict is On.
' Declare and initialize the ArrayList. Dim array_list As New ArrayList array_list.Add("Apple") array_list.Add("Banana") array_list.Add("Cherry") ... ' Array of objects. Dim obj_array() As Object obj_array = array_list.ToArray() ' Array of strings. Dim string_array() As String string_array = DirectCast(array_list.ToArray(GetType(String)), String()) ' Array object of objects. Dim astring_array As Array astring_array = array_list.ToArray()
A single ArrayList object can hold objects of many different kinds. The following code creates an ArrayList and adds a string, Form object, integer, and Bitmap to it. It then loops through the items in the list and displays their types.
Dim array_list As New ArrayList array_list.Add("What?") array_list.Add(Me) array_list.Add(1001) array_list.Add(New Bitmap(10, 10)) For Each obj As Object In array_list Debug.WriteLine(obj.GetType.ToString) Next obj
The following text shows the results:
System.String UseArrayList.Form1 System.Int32 System.Drawing.Bitmap
The value displayed for the second item depends on the name of the project (in this case, UseArrayList).
A StringCollection is similar to an ArrayList, except that it can hold only strings. Because it works only with strings, this class provides some extra type checking that the ArrayList does not. If your program tries to add an Employee object to a StringCollection, the collection raises an error.
To take advantage of this extra error checking, you should always use a StringCollection instead of an ArrayList if you are working with strings. Of course, if you need other features (such as the fast lookups provided by a Hashtable), you should use one of the classes described in the following sections.
A strongly typed collection is a collection class built to work with a particular data type. An ArrayList can store objects of any data type. A StringCollection is strongly typed to work only with strings. That gives you extra error checking that makes finding and fixing programming mistakes easier. If the program tries to insert an IncidentReport object into a StringCollection, the collection immediately raises an error and the problem is relatively easy to find.
Similarly, you can define your own collection classes that are strongly typed. For example, you could make an OrderCollection class that holds Order items. If the program tries to add a Manager or Secretary object to it, the collection raises an error.
To build a strongly typed collection from scratch, create a new class that inherits from System .Collections.CollectionBase. Inheriting from this class automatically gives your class an ArrayList object named List. It also gives your class some inherited routines that do not depend on the type of object you want the collection to hold. For example, the RemoveAt method removes the object at a specific index in the list. It doesn’t care whether the collection holds Employee objects, bitmaps, or Pizzas, so the parent class CollectionBase can implement it for you.
Your class can implement other methods for adding and retrieving items in the collection. For example, it can implement the Add, Remove, and Item methods.
Fortunately, you don’t need to build these methods from scratch. You can simply delegate them to the inherited List object. For example, the Add method can simply call List.Add, as shown in the following code:
' Add an Employee. Public Sub Add(ByVal value As Employee) List.Add(value) End Sub
This code does nothing other than call the List object’s methods. The only magic here is that the EmployeeCollection class’s Add method takes a parameter of a particular type (Employee), whereas the List object’s Add method takes a generic Object as a parameter. It is the EmployeeCollection class’s insistence on Employee objects that makes the collection strongly typed.
The Add and Item methods are about the minimum useful feature set you can provide for a strongly typed collection class.
The following table lists the standard methods provided by a strongly typed collection class. The third column indicates whether the CollectionBase parent class automatically provides the method, or whether you must delegate the method to the List object.
Method | Purpose | Provided By |
---|---|---|
Add | Adds an item to the collection | List |
Capacity | Returns the amount of space in the collection | CollectionBase |
Clear | Removes all items from the collection | CollectionBase |
Contains | Returns True if the collection contains a particular item | List |
CopyTo | Copies items from the collection into an array | List |
Count | Returns the number of items in the collection | CollectionBase |
IndexOf | Returns the index of an item | List |
InnerList | Returns an ArrayList holding the collection’s objects | CollectionBase |
Insert | Inserts an item at a specific position | List |
Item | Returns the item at a specific position | List |
List | Returns an IList holding the collection’s objects | CollectionBase |
Remove | Removes an item | List |
RemoveAt | Removes the item at a specific position | CollectionBase |
You can also add other more specialized methods if they would be useful in your application. For example, you could add methods for working with object field values rather than with the objects themselves. You might make an overloaded version of the Item method that takes as parameters a first and last name and returns the corresponding Employee object if it is in the list. You could also modify the simple Add method shown previously so that it doesn’t allow duplicates. And, you could make an Add function that takes first and last names as parameters, creates a new Employee object using those names, and returns the new object.
The following code shows a complete EmployeeCollection class. Most of its methods are straightforward delegations to the List object. The most interesting method is the Item property. This method is implemented as a read-only property, so it can include the Default keyword (which is not allowed on functions). This keyword marks Item as the class’s default property and that allows a program to access it by providing an index to an object of the class as in emp_collection(3). Notice that the Item property procedure uses DirectCast to convert the generic Object stored in List into an Employee object.
' A strongly typed collection of Employees. Public Class EmployeeCollection Inherits CollectionBase ' Add an Employee. Public Sub Add(ByVal value As Employee) List.Add(value) End Sub ' Return True if the collection contains this employee. Public Function Contains(ByVal value As Employee) As Boolean Return List.Contains(value) End Function ' Return this Employee's index. Public Function IndexOf(ByVal value As Employee) As Integer Return List.IndexOf(value) End Function ' Insert a new Employee. Public Sub Insert(ByVal index As Integer, ByVal value As Employee) List.Insert(index, value) End Sub ' Return the Employee at this position. Default Public ReadOnly Property Item(ByVal index As Integer) As Employee Get Return DirectCast(List.Item(index), Employee) End Get End Property ' Remove an Employee. Public Sub Remove(ByVal value As Employee) List.Remove(value) End Sub End Class
An additional benefit that comes with inheriting from the CollectionBase class is For Each loop support. The following code shows how a program might use this EmployeeCollection class. It creates the collection and adds five Employee objects to the list. It then uses a For Each loop to display the Employees.
Dim emp_list As New EmployeeCollection emp_list.Add(New Employee("Ann", "Anderson")) emp_list.Add(New Employee("Bart", "Baskerville")) emp_list.Add(New Employee("Candy", "Cant")) emp_list.Add(New Employee("Durk", "Distant")) emp_list.Add(New Employee("Edwina", "Evers")) For Each emp As Employee In emp_list Debug.WriteLine(emp.ToString) Next emp
Generics provide another method for building strongly typed collections. Refer to the section “Generics” later in this chapter for more information on generic collections. For more general information on generics, see Chapter 19.
The CollectionBase class enables you to build a strongly typed collection class that allows a program to store and retrieve values. In some cases, you might want a function to return a collection of objects that the calling program cannot modify. For example, suppose that your function returns a list of your company’s production locations. You don’t want the program to modify the list because it cannot change the locations. In this case, you can build a read-only strongly typed collection.
You can do this much as you build a strongly typed collection. Instead of deriving the new collection class from CollectionBase, however, derive it from the ReadOnlyCollectionBase class. Provide read-only Item methods, but do not provide any Add or Remove methods. The class itself can access its inherited InnerList object to add and remove items, but it must not give the program using your class access to that object.
Your program still needs a way to get objects into the collection, however. One method is to build the collection class in a separate library project and give it initialization methods declared with the Friend keyword. Other code in the library project could use those methods while the main program could not.
Another technique is to pass initialization data to the class’s constructor. Your code creates the collection and returns it to the main program. The main program cannot change the collection’s contents. It can create an instance of the collection of its own, but it cannot modify the one you built.
The NameValueCollection class is a collection that can hold more than one string value for a particular key (name). For example, you might use employee names as keys. The string values associated with a particular key could include extension, job title, employee ID, and so forth.
Of course, you could also store the same information by putting extension, job title, employee ID, and the other fields in an object or structure, and then storing the objects or structures in some sort of collection class such as an ArrayList. A NameValueCollection, however, is very useful if you don’t know ahead of time how many strings will be associated with each key.
The following table describes some of the NameValueCollection’s most useful properties and methods.
Property/Method | Description |
---|---|
Add | Adds a new name/value pair to the collection. If the collection already holds an entry for the name, it adds the new value to that name’s values. |
AllKeys | Returns a string array holding all of the key values. |
Clear | Removes all names and values from the collection. |
CopyTo | Copies items starting at a particular index into a one-dimensional Array object. This copies only the items (see the Item property) not the keys. |
Count | Returns the number of key/value pairs in the collection. |
Get | Gets the item for a particular index or name as a comma-separated list of values. |
GetKey | Returns the key for a specific index. |
GetValues | Returns a string array containing the values for a specific name or index. |
HasKeys | Returns True if the collection contains any non-null keys. |
Item | Gets or sets the item for a particular index or name as a comma-separated list of values. |
Keys | Returns a collection containing the keys. |
Remove | Removes a particular name and all of its values. |
Set | Sets the item for a particular index or name as a comma-separated list of values. |
The following code demonstrates some of these features. It creates a NameValueCollection and fills it with values. It associates the name Food with the values Sandwich, Salad, and Taco. It associates the name Dessert with the values Ice Cream, Pie, Cake, and Cookie. Next, the code loops through the collection’s keys. For each key, it displays the key’s name, uses the collection’s GetValues method to get an array containing the corresponding values, and then displays those values. The code then displays the values again in comma-separated lists. It again loops through the collection’s keys, this time using the
Dim nvc As New NameValueCollection nvc.Add("Food", "Sandwich") nvc.Add("Food", "Salad") nvc.Add("Food", "Taco") nvc.Add("Dessert", "Ice Cream") nvc.Add("Dessert", "Pie") nvc.Add("Dessert", "Cake") nvc.Add("Dessert", "Cookie") Dim values() As String For Each key As String In nvc.Keys Debug.WriteLine(key & ":") values = nvc.GetValues(key) For Each value As String In values Debug.WriteLine(" " & value) Next value Next key Debug.WriteLine("*****") For Each key As String In nvc.Keys Debug.WriteLine(key & ": " & nvc.Item(key)) Next key
The following text shows the result:
Food: Sandwich Salad Taco Dessert: Ice Cream Pie Cake Cookie ***** Food: Sandwich,Salad,Taco Dessert: Ice Cream,Pie,Cake,Cookie
Note that there is no easy way to remove a particular value from a name. For example, it’s not trivial to remove the value Pie from the name Dessert. The following statement shows one method, although you would need to modify it slightly if you didn’t know whether the Pie entry was last in the list (so it might or might not be followed by a comma):
nvc.Item("Dessert") = nvc.Item("Dessert").Replace("Pie,", "")