Chapter 17. Using Collections and Generics

The .NET Framework provides a rich suite of collection classes. With the advent of Generics in 2.0, most of these collection classes are now type safe, making for a greatly enhanced programming experience. The collection classes include the List, Dictionary, Sorted Dictionary, Queue, and Stack.

The simplest collection is the Array, the only collection type for which Visual Basic 2005 provides built-in support. In this chapter, you will learn to work with single, multidimensional, and jagged arrays . Arrays have built-in indexers, allowing you to request the nth member of the array.

The .NET Framework provides a number of interfaces, such as IEnumerable and ICollection, whose implementation provides you with standard ways to interact with collections. In this chapter, you will see how to work with the most essential of these. The chapter concludes with a tour of commonly used .NET collections, including List, Dictionary, Queue, and Stack.

Tip

In previous versions of Visual Basic.NET, the collection objects were not type safe (you could, for example, mix strings and integers in a Dictionary). The non-type-safe version of List (ArrayList), Dictionary, Queue, and Stack are still available for backward compatibility, but will not be covered in this book because their use is very similar to the Generics-based versions, and because they are obsolete and deprecated.

Arrays

An array is an indexed collection of objects, all of the same type. Arrays are both built into the language and implemented as types, with properties, fields, and methods.

Tip

Arrays do not use the new Generic syntax. Generics are covered later in this chapter.

Visual Basic 2005 provides native syntax for the declaration of Arrays. What is actually created, however, is an object of type System.Array . Arrays in Visual Basic 2005 provide you with the best of both worlds: easy-to-use array syntax underpinned with an actual class definition, so that instances of an array have access to the methods and properties of System.Array. These appear in Table 17-1.

Table 17-1. System.Array methods and properties

Method or property

Purpose

BinarySearch

Overloaded public static method that searches a one-dimensional sorted array.

Clear

Public static method that sets a range of elements in the array either to zero or to a null reference.

Copy

Overloaded public static method that copies a section of one array to another array.

CreateInstance

Overloaded public static method that instantiates a new instance of an array.

IndexOf

Overloaded public static method that returns the index (offset) of the first instance of a value in a one-dimensional array.

LastIndexOf

Overloaded public static method that returns the index of the last instance of a value in a one-dimensional array.

Reverse

Overloaded public static method that reverses the order of the elements in a one-dimensional array.

Sort

Overloaded public static method that sorts the values in a one-dimensional array.

IsFixedSize

Required because Array implements ICollection. With Arrays, this will always return True (all arrays are of a fixed size).

IsReadOnly

Public property (required because Array implements IList) that returns a Boolean value indicating whether the array is read-only.

IsSynchronized

Public property (required because Array implements ICollection) that returns a Boolean value indicating whether the array is thread safe.

Length

Public property that returns the length of the array.

Rank

Public property that returns the number of dimensions of the array.

SyncRoot

Public property that returns an object that can be used to synchronize access to the array.

GetEnumerator

Public method that returns an IEnumerator.

GetLength

Public method that returns the length of the specified dimension in the array.

GetLowerBound

Public method that returns the lower boundary of the specified dimension of the array.

GetUpperBound

Public method that returns the upper boundary of the specified dimension of the array.

Initialize

Initializes all values in a value type array by calling the default constructor for each value. With reference arrays, all elements in the array are set to null.

SetValue

Overloaded public method that sets the specified array elements to a value.

Declaring Arrays

Declare a Visual Basic 2005 array with the following syntax:

Dim array-name() as type

For example:

Dim intArray() as Integer

Tip

You are not actually declaring an array. Technically, you are declaring a variable (intArray) that will hold a reference to an Array of integers. As always, we’ll use the shorthand and refer to intArray as the array, knowing that what we really mean is that it is a varaible that holds a reference to an (unnamed) array on the heap.

The parentheses tell the Visual Basic 2005 compiler that you are declaring an array, and the type specifies the type of the elements it will contain. In the previous example, intArray is an array of integers.

You instantiate an array using the new keyword. For example:

intArray = new Integer(5) {  }

Tip

The braces are required and indicate that you are not explicitly initializing the array with any values at this time.

The effect of this declaration is to create and intialize an array of six integers, all of which are initialized to the value zero.

Warning

VB6 Warning: Although the value in VB6 designated the upper bound of the array, as it does in VB.NET, the array indexes were one based (by default). Therefore, the upper bound was also the size of the array. in Visual Basic 2005, arrays are zero-based, and so the size of the array is one more than the upper bound.

C# Warning: In Visual Basic 2005, the value passed into the parentheses is not the size of the array, but the upper bound.

It is important to distinguish between the array itself (which is a collection) and the elements of the array. intArray is the array (or, more accurately, the variable that holds the reference to the array); its elements are the six integers it holds.

Visual Basic 2005 arrays are reference types, created on the heap. Thus, the array to which intArray refers is allocated on the heap. The elements of an array are allocated based on their own type. Since Integers are value types, the elements in intArray will be value types, not boxed integers, and, thus, all the elements will be created inside the block of memory allocated for the array.

The block of memory allocated to an array of reference types will contain references to the actual elements, which are themselves created on the heap in memory separate from that allocated for the array.

Understanding Default Values

When you create an array of value types, each element initially contains the default value for the type stored in the array. The statement:

intArray = new Integer(5) {  }

creates an array of six integers, each of whose value is set to 0, which is the default value for integer types.

On the other hand reference types in an array are not initialized to their default value. Instead, the references held in the array are initialized to null. If you attempt to access an element in an array of reference types before you have specifically initialized the elements, you will generate an exception.

Assume you have created a Button class. Declare an array of Button objects with the following statement:

Dim myButtonArray() as Button

and instantiate the actual array like this:

myButtonArray = new Button(3)

You can shorten this to:

Dim myButtonArray() as Button = new Button(3)

This statement does not create an array with references to four Button objects. Instead, this creates the array myButtonArray with four null references. To use this array, you must first construct and assign the four Button objects, one for each reference in the array.

Accessing Array Elements

Access the elements of an array using the index operator (()). Arrays are zero-based, which means that the index of the first element is always zero—in this case, myArray(0).

As explained previously, arrays are objects and, thus, have properties. One of the more useful of these is Length, which tells you how many objects are in an array. Array objects can be indexed from 0 to Length-1. That is, if there are five elements in an array; their indices are 0, 1, 2, 3, and 4.

Example 17-1 is a console application named Arrays, that illustrates the array concepts covered so far.

Example 17-1. Working with Arrays
Module Module1

    Public Class Employee
        Private _empID As String
        Public Sub New(ByVal empID As Integer)
            Me._empID = empID
        End Sub
        Public Overrides Function ToString() As String
            Return _empID
        End Function
    End Class

    Sub Main()
        Dim empArray() As Employee
        empArray = New Employee(3) {  }
        Dim intArray() As Integer = New Integer(5) {  }

        For index As Integer = 0 To empArray.Length − 1
            empArray(index) = New Employee(index + 5)
        Next

        For index As Integer = 0 To intArray.Length − 1
            Console.WriteLine(intArray(index).ToString())
        Next

        For index As Integer = 0 To empArray.Length − 1
            Console.WriteLine(empArray(index).ToString())
        Next
    End Sub

End Module

Output:
0
0
0
0
0
0
5
6
7
8

The example starts with the definition of an Employee class, which implements a constructor taking a single integer parameter. The ToString() method inherited from Object is overridden to print the value of the Employee object’s employee ID.

The test method declares and then instantiates a pair of arrays. The integer array is automatically filled with integers whose value is set to 0. The Employee array contents must be constructed by hand.

Finally, the contents of the arrays are printed to ensure that they are filled as intended. The six integers print their value (zero) first, followed by the four Employee objects.

Initializing Array Elements

It is possible to initialize the contents of an array at the time it is instantiated by providing a list of values delimited by curly braces ({ }). Visual Basic 2005 provides a longer and a shorter syntax:

Dim firstArray()  As Integer = New Integer(5) {1, 2, 3, 4, 5, 6}
Dim secondArray() As Integer = {1, 2, 3, 4, 5, 6}

There is no practical difference between these two statements, and most programmers will use the shorter syntax.

Multidimensional Arrays

Arrays can be thought of as long rows of slots into which values can be placed. Once you have a picture of a row of slots, imagine 10 rows, one on top of another. This is the classic two-dimensional array of rows and columns (often referred to as a Matrix).[*]

A third dimension is possible, but somewhat harder to imagine. Okay, now imagine four dimensions. Now imagine 10.

Those of you who are not string-theory physicists have probably given up, as have I. Multidimensional arrays are useful, however, even if you can’t quite picture what they would look like.

Visual Basic 2005 supports two types of multidimensional arrays: rectangular and jagged. In a rectangular array, every row is the same length. A jagged array, however, is an array of arrays, each of which can be a different length.

Rectangular arrays

A rectangulararray is an array of two (or more) dimensions. In the classic two- dimensional array, the first dimension is the rows and the second dimension is the columns.

To declare a two-dimensional array, use the following syntax:

Dim identifier(,) As type

For example, to declare and instantiate a two-dimensional rectangular array named theMatrix that contains four rows and three columns of integers, you would write:

Dim theMatrix(,) As Integer = New Integer(3,2) { }

Example 17-2 declares, instantiates, initializes, and prints the contents of a two- dimensional array. In this example, a nested For loop is used to initialize the elements of the array.

Example 17-2. theMatrix
Module Module1

    Sub Main()
        Const rows As Integer = 3
        Const columns As Integer = 2

        Dim theMatrix(,) As Integer = New Integer(rows, columns) {  }
        For index As Integer = 0 To rows
            For internalIndex As Integer = 0 To columns
                theMatrix(index, internalIndex) = _
                     (index + 1) * (internalIndex + 1)
            Next
        Next

        For index As Integer = 0 To rows
            For internalIndex As Integer = 0 To columns
                Console.WriteLine("theMatrix({0})({1}) = {2}", _
                index, internalIndex, theMatrix(index, internalIndex))
            Next
        Next


    End Sub

End Module

Output:
theMatrix(0)(0) = 1
theMatrix(0)(1) = 2
theMatrix(0)(2) = 3
theMatrix(1)(0) = 2
theMatrix(1)(1) = 4
theMatrix(1)(2) = 6
theMatrix(2)(0) = 3
theMatrix(2)(1) = 6
theMatrix(2)(2) = 9
theMatrix(3)(0) = 4
theMatrix(3)(1) = 8
theMatrix(3)(2) = 12

In this example, you declare a pair of constant values:

Const rows As Integer = 3
Const columns As Integer = 2

that are then used to set the size of the dimensions the array:

Dim theMatrix(,) As Integer = New Integer(rows, columns) {  }

Notice the syntax. The parentheses in the theMatrix(,) declaration indicate that the array has two dimensions. (Two commas would indicate three dimensions, and so on.) The actual instantiation of theMatrix with New Integer(rows, columns) { } allocates the memory and sets the size of each dimension.

The program fills the rectangle with a pair of nested For loops, iterating through each column in each row. Thus, the first element filled is theMatrix(0,0), followed by theMatrix(0,1), and theMatrix(0,2). Once this is done, the program moves on to the next rows: theMatrix(1,0), theMatrix(1,1), theMatrix(1,2), and so forth, until all the columns in all the rows are filled.

Just as you can initialize a one-dimensional array using lists of values, you can initialize a two-dimensional array using similar syntax. Thus, you can modify the previous example to initialize the values at the same time you declare them:

Dim theMatrixReloaded(,) As Integer = New Integer(rows, columns) _
{ _
    {0, 1, 2}, {3, 4, 5}, {6, 7, 8}, {9, 10, 11} _
}

Jagged Arrays

A jaggedarray is an array of arrays. It is called “jagged " because the rows need not be the same size, and thus a graphical representation of the array would not be square.

When you create a jagged array, you declare the number of rows in your array. Each row will hold an array, which can be of any length. These arrays must each be declared. You can then fill in the values for the elements in these “inner” arrays.

In a jagged array, each dimension is a one-dimensional array. To declare a jagged array, use the following syntax, where the number of parentheses indicates the number of dimensions of the array:

Dim identifier()() as type

For example, you would declare a two-dimensional jagged array of integers named myJaggedArray as follows:

Dim myJaggedArray()() as Integer = new Integer(5)

Access the fifth element of the third array by writing myJaggedArray(2)(4).

Example 17-3 creates a jagged array named myJaggedArray, initializes its elements, and then prints their content. To save space, the program takes advantage of the integer array elements automatically initializing to zero, and it initializes the values of only some of the elements.

Example 17-3. Working with a jagged array
Module Module1

    Sub Main()
        Const rows As Integer = 3

        ''declare the jagged array as 4 rows high
        Dim jaggedArray As Integer()() = New Integer(rows)() {  }

        '' the first row has 3 elements
        jaggedArray(0) = New Integer(2) {  }

        '' the second row has 2 elements
        jaggedArray(1) = New Integer(1) {  }

        '' the third row has 4 elements
        jaggedArray(2) = New Integer(3) {  }

        '' the fourth row has 5 elements
        jaggedArray(3) = New Integer(4) {  }

        '' fill some (not all) elements of the rows
        jaggedArray(0)(2) = 15
        jaggedArray(1)(1) = 12
        jaggedArray(2)(1) = 9
        jaggedArray(2)(2) = 99
        jaggedArray(3)(0) = 10
        jaggedArray(3)(1) = 11
        jaggedArray(3)(2) = 12
        jaggedArray(3)(3) = 13
        jaggedArray(3)(4) = 14

        For index As Integer = 0 To jaggedArray(0).Length − 1
            Console.WriteLine("jaggedArray(0)(" & index & "): {0}", _
                jaggedArray(0)(index))
        Next

        For index As Integer = 0 To jaggedArray(1).Length − 1
            Console.WriteLine("jaggedArray(1)(" & index & "): {0}", _
                jaggedArray(1)(index))
        Next

        For index As Integer = 0 To jaggedArray(2).Length − 1
            Console.WriteLine("jaggedArray(2)(" & index & "): {0}", _
                jaggedArray(2)(index))
        Next

        For index As Integer = 0 To jaggedArray(3).Length − 1
            Console.WriteLine("jaggedArray(3)(" & index & "): {0}", _
                jaggedArray(3)(index))
        Next

    End Sub

End Module

Output:
jaggedArray(0)(0): 0
jaggedArray(0)(1): 0
jaggedArray(0)(2): 15
jaggedArray(1)(0): 0
jaggedArray(1)(1): 12
jaggedArray(2)(0): 0
jaggedArray(2)(1): 9
jaggedArray(2)(2): 99
jaggedArray(2)(3): 0
jaggedArray(3)(0): 10
jaggedArray(3)(1): 11
jaggedArray(3)(2): 12
jaggedArray(3)(3): 13
jaggedArray(3)(4): 14

In this example, a jagged array is created with four rows:

Dim jaggedArray As Integer()() = New Integer(rows)() {  }

Notice that the second dimension is not specified. Each row holds an array and each of these arrays can have a different size. Indeed, you see that the first has three rows, the second has two, and so forth.

Once an array is specified for each row, you need only populate the various members of each array and then print their contents to ensure that all went as expected.

Tip

When you access the members of the rectangular array, you put the indexes all within one set of parentheses:

theMatrixtheMatrix(index, innerIndex)

while with a jagged array you need a pair of parentheses:

jaggedArray(3)(index)

You can keep this straight by thinking of the first as a single array of more than one dimension and the jagged array as an array ofarrays.

Generics

The goal of generics is to create collections that are “type safe” but that can be reused at design time with any type. Thus, the designer of a generic List would like to be able to designate that the List class can hold any type of data, but a specific type will be declared when the List class is used, and the compiler will enforce the type safety.

Generic List Class

The classic problem with the Array type is its fixed size. If you do not know in advance how many objects an array will hold, you run the risk of declaring either too small an array (and running out of room) or too large an array (and wasting memory).

Suppose you create a program that gathers input from a web site. As you find objects (strings, books, values, etc.), you will add them to the array, but you have no idea how many objects you’ll collect in any given session. The classic fixed-size array is not a good choice, as you can’t predict how large an array you’ll need.

The List class is like an array whose size is dynamically increased as required. Lists provide a number of useful methods and properties. Some of the most important are shown in Table 17-2.

Table 17-2. List methods and properties

Method or property

Purpose

The idiom in the Framework Class Library is to provide an Item property for collection classes that is implemented as an indexer in Visual Basic 2005.

Capacity

Property to get or set the number of elements the List can contain. This value is increased automatically if count exceeds capacity. You might set this value to reduce the number of reallocations, and you may call Trim to reduce this value to the actual Count.

Count

Property to get the number of elements currently in the array.

Item

Gets or sets the element at the specified index. This is the indexer for the List class. a

Add

Public method to add an object to the List.

AddRange

Public method that adds the elements of an ICollection to the end of the List.

BinarySearch

Overloaded public method that uses a binary search to locate a specific element in a sorted List.

Clear

Removes all elements from the List.

Contains

Determines if an element is in the List.

CopyTo

Overloaded public method that copies a List to a one-dimensional array.

Exists

Determines if an element is in the List.

Find

Returns the first occurrence of the element in the List.

FindAll

Returns all the specified elements in the List.

GetEnumerator

Overloaded public method that returns an enumerator to iterate through a List.

GetRange

Copies a range of elements to a new List.

IndexOf

Overloaded public method that returns the index of the first occurrence of a value.

Insert

Inserts an element into List.

InsertRange

Inserts the elements of a collection into the List.

LastIndexOf

Overloaded public method that returns the index of the last occurrence of a value in the List.

Remove

Removes the first occurrence of a specific object.

RemoveAt

Removes the element at the specified index.

RemoveRange

Removes a range of elements.

Reverse

Reverses the order of elements in the List.

Sort

Sorts the List.

ToArray

Copies the elements of the List to a new array.

TrimToSize

Sets the capacity to the actual number of elements in the List.

When you create a List, you do not define how many objects it will contain. Add to the List using the Add method, and the List takes care of its own internal bookkeeping, as illustrated in Example 17-4.

Example 17-4. Working with a List
Imports System.Collections.Generic
Module Module1

   Sub Main()
      Dim empList As New List(Of Employee)
      Dim intList As New List(Of Integer)

      For counter As Integer = 0 To 4
         empList.Add(New Employee(counter + 100))
         intList.Add(counter + 5)
      Next


      For Each val As Integer In intList
         Console.Write(val.ToString() + " ")
      Next
      Console.WriteLine(Environment.NewLine)

      For Each emp As Employee In empList
         Console.Write(emp.ToString() + " ")
      Next
      Console.WriteLine(Environment.NewLine)
      Console.WriteLine("empList.Capacity: {0}", empList.Capacity)

   End Sub

End Module

Public Class Employee

   Private employeeID As Integer

   Public Sub New(ByVal theID As Integer)
      Me.employeeID = theID
   End Sub
   Public Overrides Function ToString() As String
      Return employeeID.ToString()
   End Function
   Public Property EmpID() As Integer
      Get
         Return employeeID
      End Get
      Set(ByVal value As Integer)
         employeeID = value
      End Set
   End Property

End Class

Output:
0 5 10 15 20
100 101 102 103 104
empArray.Capacity: 8

With an Array class, you define how many objects the array will hold. If you try to add more than that, the Array class will throw an exception. With a List, you do not declare how many objects the List will hold. The List has a property, Capacity, which is the number of elements the List is capable of storing:

public int Capacity { get; set; }

The default capacity is eight. When you add the ninth element, the capacity is automatically doubled to 16. If you change the For loop to:

For counter As Integer = 0 To 8

the output looks like this:

5 6 7 8 9 10 11 12 13
100 101 102 103 104 105 106 107 108
empList.Capacity: 16

You can manually set the capacity to any number equal to or greater than the count. If you set it to a number less than the count, the program will throw an exception of type ArgumentOutOfRangeException.

Implementing IComparable

Like all collections, the List implements the Sort method, which allows you to sort any objects that implement IComparable. In the next example, you’ll modify the Employee object to implement IComparable:

Public Class Employee
   Implements IComparable(Of Employee)

To implement the IComparable interface, the Employee object must provide a CompareTo method:

Function CompareTo(ByVal rhs As Employee) As Integer _
Implements IComparable(Of IComparable.Employee).CompareTo
   Return Me.employeeID.CompareTo(rhs.employeeID)
End Function

The CompareTo method has been implemented to take an Employee as a parameter. The current Employee object must compare itself to the parameter and return -1 if it is smaller than the parameter, 1 if it is greater than the parameter, and 0 if it is equal to the parameter.

Tip

It is up to Employee to determine what smaller than, greater than, and equal to actually mean. In the next example, you’ll compare the EmployeeID (so that Employees can be sorted by EmployeeIDs.).

You are ready to sort the list of employees, empList. To see if the sort is working, you’ll need to add integers and Employee instances to their respective lists with (pseudo)random values. To create the random values, you’ll instantiate an object of class Random and call the Next method on the Random to return a pseudo-random number.

The Next method is overloaded; one version allows you to pass in an integer that represents the largest random number you want. In this case, you’ll pass in the value 10 to generate a random number between 0 and 10:

Random r = new Random();
r.Next(10);

Example 17-5 creates and then sorts the two lists.

Example 17-5. Sorting generic Lists usng IComparable
Imports System.Collections.Generic
Module Module1

   Sub Main()
      Dim empList As New List(Of Employee)
      Dim intList As New List(Of Integer)

      Dim r As New Random()

      For counter As Integer = 0 To 8
         empList.Add(New Employee(r.Next(10) + 100))
         intList.Add(r.Next(10))
      Next


      For Each val As Integer In intList
         Console.Write(val.ToString() + " ")
      Next
      Console.WriteLine(Environment.NewLine)

      For Each emp As Employee In empList
         Console.Write(emp.ToString() + " ")
      Next
      Console.WriteLine(Environment.NewLine)

      intList.Sort()
      empList.Sort()
      Console.WriteLine("Sorted: ")
      For Each val As Integer In intList
         Console.Write(val.ToString() + " ")
      Next
      Console.WriteLine(Environment.NewLine)

      For Each emp As Employee In empList
         Console.Write(emp.ToString() + " ")
      Next

   End Sub

End Module
Public Class Employee
   Implements IComparable(Of Employee)

   Private employeeID As Integer

   Public Sub New(ByVal theID As Integer)
      Me.employeeID = theID
   End Sub
   Public Overrides Function ToString() As String
      Return employeeID.ToString()
   End Function
   Public Property EmpID() As Integer
      Get
         Return employeeID
      End Get
      Set(ByVal value As Integer)
         employeeID = value
      End Set
   End Property

   Function CompareTo(ByVal rhs As Employee) As Integer _
       Implements IComparable(Of IComparable.Employee).CompareTo
      Return Me.employeeID.CompareTo(rhs.employeeID)
   End Function

End Class
Output:
5 3 7 8 3 8 4 9 7
102 106 100 106 101 107 109 109 106

Sorted:
3 3 4 5 7 7 8 8 9
100 101 102 106 106 106 107 109 109

The output shows that the integer array and Employee array were generated with random numbers. When sorted, the display shows the values have been ordered properly.

Queues

A queue represents a first-in, first-out (FIFO) collection. The classic analogy is to a line (or queue if you are British) at a ticket window. The first person in line ought to be the first person to come off the line to buy a ticket.

A queue is a good collection to use when you are managing a limited resource. For example, you might want to send messages to a resource that can only handle one message at a time. You would then create a message queue so that you can say to your clients: “Your message is important to us. Messages are handled in the order in which they are received.”

The Queue class has a number of member methods and properties, as shown in Table 17-3.

Table 17-3. Queue methods and properties

Method or property

Purpose

Count

Public property that gets the number of elements in the Queue

Clear

Removes all objects from the Queue

Contains

Determines if an element is in the Queue

CopyTo

Copies the Queue elements to an existing one-dimensional array

Dequeue

Removes and returns the object at the beginning of the Queue

Enqueue

Adds an object to the end of the Queue

GetEnumerator

Returns an enumerator for the Queue

Peek

Returns the object at the beginning of the Queue without removing it

ToArray

Copies the elements to a new array

Add elements to your queue with the Enqueue command and take them off the queue with Dequeue. You can also see what is next in the queue (without removing it) using Peek. Example 17-6 illustrates.

Example 17-6. Working with a queue
Imports System.Collections.Generic
Module Module1

   Sub Main()
      Dim empQueue As New Queue(Of Employee)
      Dim intQueue As New Queue(Of Integer)

      Dim r As New Random()

      For counter As Integer = 0 To 8
         empQueue.Enqueue(New Employee(r.Next(10) + 100))
         intQueue.Enqueue(r.Next(10))
      Next

      '' Display the queues
      Console.Write("intQueue: ")
      PrintValues(intQueue)
      Console.WriteLine()
      Console.Write("empQueue: ")
      PrintValues(empQueue)
      Console.WriteLine()

      '' remove one element from the queues
      Console.WriteLine(vbCrLf + "intQueue.Dequeue {0}" + vbTab, _
         intQueue.Dequeue())
      Console.WriteLine("empQueue.Dequeue {0}" + vbTab, empQueue.Dequeue())


      '' Display the queues
      Console.Write("intQueue: ")
      PrintValues(intQueue)
      Console.WriteLine()
      Console.Write("empQueue: ")
      PrintValues(empQueue)
      Console.WriteLine()

      '' remove another element from the queues
      Console.WriteLine(vbCrLf + "intQueue.Dequeue {0}" + vbTab, _
        intQueue.Dequeue())
      Console.WriteLine("empQueue.Dequeue {0}" + vbTab, empQueue.Dequeue())

      '' Display the queues
      Console.Write("intQueue: ")
      PrintValues(intQueue)
      Console.WriteLine()
      Console.Write("empQueue: ")
      PrintValues(empQueue)
      Console.WriteLine()

      '' peek at the first element remaining
      Console.WriteLine(vbCrLf + "intQueue.Peek {0}" + vbTab, _
        intQueue.Peek())
      Console.WriteLine("empQueue.Peek {0}" + vbTab, empQueue.Peek())

      '' Display the queues
      Console.Write("intQueue: ")
      PrintValues(intQueue)
      Console.WriteLine()
      Console.Write("empQueue: ")
      PrintValues(empQueue)
      Console.WriteLine()



   End Sub
   Public Sub PrintValues(ByVal myCollection As IEnumerable(Of Integer))
      Dim myEnumerator As IEnumerator(Of Integer) = _
        myCollection.GetEnumerator()
      While myEnumerator.MoveNext()
         Console.Write("{0} ", myEnumerator.Current)
      End While
   End Sub

   Public Sub PrintValues(ByVal myCollection As IEnumerable(Of Employee))
      Dim myEnumerator As IEnumerator(Of Employee) = _
        myCollection.GetEnumerator()
      While myEnumerator.MoveNext()
         Console.Write("{0} ", myEnumerator.Current)
      End While
   End Sub



End Module
Public Class Employee
   Implements IComparable(Of Employee)
   Private employeeID As Integer

   Public Sub New(ByVal theID As Integer)
      Me.employeeID = theID
   End Sub

   Public Overrides Function ToString() As String
      Return employeeID.ToString()
   End Function

   Public Property EmpID() As Integer
      Get
         Return employeeID
      End Get
      Set(ByVal value As Integer)
         employeeID = value
      End Set
   End Property

   Function CompareTo(ByVal rhs As Employee) As Integer _
     Implements IComparable(Of WorkingWithQueues.Employee).CompareTo
      Return Me.employeeID.CompareTo(rhs.employeeID)
   End Function

End Class

Output:
intQueue: 7 2 6 3 6 6 8 6 2
empQueue: 108 101 106 101 100 107 108 103 108

intQueue.Dequeue 7
empQueue.Dequeue 108
intQueue: 2 6 3 6 6 8 6 2
empQueue: 101 106 101 100 107 108 103 108

intQueue.Dequeue 2
empQueue.Dequeue 101
intQueue: 6 3 6 6 8 6 2
empQueue: 106 101 100 107 108 103 108

intQueue.Peek 6
empQueue.Peek 106
intQueue: 6 3 6 6 8 6 2
empQueue: 106 101 100 107 108 103 108

Tip

VB6 Tip: In this example, I’ve used the traditional VbCrLf constants rather than Environment.NewLine as in previous examples. Visual Basic 2005 supports both. There is no Environment class equivalent to vbTab.

Because the Queue class is enumerable, you can pass it to the PrintValues method, which expects a reference to an IEnumerable interface. The conversion is implicit. In the PrintValues method you call GetEnumerator, which you will remember is the single method of all IEnumerable classes. This returns an IEnumerator, which you then use to enumerate all the objects in the collection.

Stacks

A stack is a last-in, first-out (LIFO) collection, like a stack of dishes at a buffet table, or a stack of coins on your desk. The last dish added to the top of the stack is the first dish you take off the stack.

The principal methods for adding to and removing from a stack are Push and Pop; Stack also offers a Peek method, very much like Queue. The significant methods and properties for Stack are shown in Table 17-4.

Table 17-4. Stack methods and properties

Method or property

Purpose

Count

Public property that gets the number of elements in the Stack

Clear

Removes all objects from the Stack

Clone

Creates a shallow copy

Contains

Determines if an element is in the Stack

CopyTo

Copies the Stack elements to an existing one-dimensional array

GetEnumerator

Returns an enumerator for the Stack

Peek

Returns the object at the top of the Stack without removing it

Pop

Removes and returns the object at the top of the Stack

Push

Inserts an object at the top of the Stack

ToArray

Copies the elements to a new array

Dictionaries

A dictionary is a collection that associates a key with a value. A language dictionary, such as Webster’s, associates a word (the key) with its definition (the value).

To see the benefit of dictionaries , start by imagining that you want to keep a list of the state capitals. One approach might be to put them in an array:

Dim stateCapitals(50) As String

The stateCapitals array will hold 50 state capitals. Each capital is accessed as an offset into the array. For example, to access the capital for Arkansas, you need to know that Arkansas is the fourth state in alphabetical order:

Dim capitalOfArkansas As String = stateCapitals(4)

It is inconvenient, however, to access state capitals using array notation. After all, if I need the capital for Massachusetts, there is no easy way for me to determine that Massachusetts is the 21st state alphabetically.

It would be far more convenient to store the capital with the state name. A dictionary allows you to store a value (in this case, the capital) with a key (in this case, the name of the state).

A .NET Framework generic dictionary can associate any kind of key (string, integer, object, etc.) with any kind of value (string, integer, object, etc.). Typically, of course, the key is fairly short, the value fairly complex.

The most important attributes of a good dictionary are that it is easy to add values and that it is quick to retrieve values and some of the most important properties methods of the Dictionary class are shown in Table 17-5.

Table 17-5. Dictionary methods and properties

Method or property

Purpose

Count

Public property that gets the number of elements in the Dictionary.

Item

The indexer for the Dictionary.

Keys

Public property that gets a collection containing the keys in the Dictionary. (See also Values.)

Values

Public property that gets a collection containing the values in the Dictionary. (See also Keys.)

Add

Adds an entry with a specified Key and Value.

Clear

Removes all objects from the Dictionary.

ContainsKey

Determines whether the Dictionary has a specified key.

ContainsValue

Determines whether the Dictionary has a specified value.

GetEnumerator

Returns an enumerator for the Dictionary.

GetObjectData

Implements ISerializable and returns the data needed to serialize the Dictionary.

Remove

Removes the entry with the specified Key.

The key in a Dictionary can be a primitive type, or it can be an instance of a user-defined type (an object). Objects used as keys for a Dictionary must implement GetHashCode as well as Equals. In most cases, you can simply use the inherited implementation from Object.

IDictionary IDictionary

Dictionaries implement the IDictionary<K,V> interface (where K is the key type and V is the value type).

Example 17-7 demonstrates adding items to a Dictionary and then retrieving them.

Example 17-7. The Item property as offset operators
Module Module1

    Sub Main()
      Dim dict As Dictionary(Of String, String) = New Dictionary(Of String, String)
      dict.Add("Alabama", "Montgomery")
      dict.Add("Alaska", "Juneau")
      dict.Add("Arizona", "Phoenix")
      dict.Add("Arkansas", "Little Rock")
      dict.Add("California", "Sacramento")
      dict.Add("Colorado", "Denver")
      dict.Add("Connecticut", "Hartford")
      dict.Add("Delaware", "Dover")
      dict.Add("Florida", "Tallahassee")
      dict.Add("Georgia", "Atlanta")
      dict.Add("Hawaii", "Honolulu")
      dict.Add("Idaho", "Boise")
      dict.Add("Illinois", "Springfield")
      dict.Add("Iowa", "Des Moines")
      dict.Add("Kansas", "Topeka")
      dict.Add("Kentucky", "Frankfort")
      dict.Add("Louisiana", "Baton Rouge")
      dict.Add("Maine", "Augusta")
      dict.Add("Maryland", "Anapolis")
      dict.Add("Massachusetts", "Boston")
      dict.Add("Michigan", "Lansing")
      dict.Add("Minnesota", "St. Paul")
      dict.Add("Mississippi", "Jackson")
      dict.Add("Missouri", "Jefferson City")
      dict.Add("Montana", "Helena")
      dict.Add("Nebraska", "Lincoln")
      dict.Add("Nevada", "Carson City")
      dict.Add("New Hampshire", "Concord")
      dict.Add("New Jersey", "Trenton")
      dict.Add("New Mexico", "Santa Fe")
      dict.Add("New York", "Albany")
      dict.Add("North Carolina", "Raleigh")
      dict.Add("North Dakota", "Bismark")
      dict.Add("Ohio", "Columbus")
      dict.Add("Oklahoma", "Oklahoma City")
      dict.Add("Oregon", "Salem")
      dict.Add("Pennsylvania", "Harrisburg")
      dict.Add("Rhode Island", "Providence")
      dict.Add("South Carolina", "Columbia")
      dict.Add("South Dakota", "Pierre")
      dict.Add("Tennessee", "Nashville")
      dict.Add("Texas", "Austin")
      dict.Add("Utah", "Salt Lake City")
      dict.Add("Vermont", "Montpelier")
      dict.Add("Washington", "Olympia")
      dict.Add("West Virginia", "Charleston")
      dict.Add("Wisconsin", "Madison")
      dict.Add("Wyoming", "Cheyenne")

      ' access a state
      Console.WriteLine("The capital of Massachusetts is {0}", _
         dict("Massachusetts"))
    End Sub
End Module

Output:
The capital of Massachussetts is Boston

Example 17-7 begins by instantiating a new Dictionary. The key and the value are both declared to be strings.

I got a bit carried away and added the key/value pairs for all 50 states. The key is the state name, the value is the capital (often the values associated with a key will be more complex than simple strings) then accessed the capital of Massachusetts, using the string Massachusetts as the key, and retrieving the string Boston as the value.

Warning

If you use a reference type as a key you must be careful not to change the value of the key object once you are using it in a Dictionary. (This can happen by changing another reference to the same object and inadvertently changing the value of the key)

If, for example, you were using an Employee object as a key, changing the employeeID in another reference to the same object would create problems in the Dictionary if that property were used by the Equals or GetHashCode methods; the Dictionary consults these methods.



[*] “The Matrix is everywhere. It is all around us. Even now in this very room. You can see it when you look out your window. Or when you turn on your television. You can feel it when you go to work. When you go to Church. When you pay your taxes. It is the world that has been pulled over your eyes to blind you from the truth.” (Morpheus, The Matrix, Warner Brothers, 1999)

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset