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
.
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.
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.
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.
Declare a Visual Basic 2005 array with the following syntax:
Dimarray-name
() astype
For example:
Dim intArray() as Integer
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) { }
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.
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.
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.
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.
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.
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.
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.
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:
Dimidentifier
(,) Astype
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.
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} _ }
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:
Dimidentifier
()() astype
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.
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.
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.
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.
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. List
s provide a number of useful methods and properties. Some of the most important are shown in Table 17-2.
Method or property |
Purpose |
The idiom in the Framework Class Library is to provide an | |
|
Property to get or set the number of elements the |
|
Property to get the number of elements currently in the array. |
|
Gets or sets the element at the specified index. This is the indexer for the |
|
Public method to add an object to the |
|
Public method that adds the elements of an |
|
Overloaded public method that uses a binary search to locate a specific element in a sorted |
|
Removes all elements from the |
|
Determines if an element is in the |
|
Overloaded public method that copies a |
|
Determines if an element is in the |
|
Returns the first occurrence of the element in the |
|
Returns all the specified elements in the |
|
Overloaded public method that returns an enumerator to iterate through a |
|
Copies a range of elements to a new |
|
Overloaded public method that returns the index of the first occurrence of a value. |
|
Inserts an element into |
|
Inserts the elements of a collection into the |
|
Overloaded public method that returns the index of the last occurrence of a value in the |
|
Removes the first occurrence of a specific object. |
|
Removes the element at the specified index. |
|
Removes a range of elements. |
|
Reverses the order of elements in the |
|
Sorts the |
|
Copies the elements of the |
|
Sets the capacity to the actual number of elements in the |
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.
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
.
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.
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 EmployeeID
s.).
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.
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.
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.
Method or property |
Purpose |
|
Public property that gets the number of elements in the |
|
Removes all objects from the |
|
Determines if an element is in the |
|
Copies the |
|
Removes and returns the object at the beginning of the |
|
Adds an object to the end of the |
|
Returns an enumerator for the |
|
Returns the object at the beginning of the |
|
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.
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
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.
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.
Method or property |
Purpose |
|
Public property that gets the number of elements in the |
|
Removes all objects from the |
|
Creates a shallow copy |
|
Determines if an element is in the |
|
Copies the |
|
Returns an enumerator for the |
|
Returns the object at the top of the |
|
Removes and returns the object at the top of the |
|
Inserts an object at the top of the |
|
Copies the elements to a new array |
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.
Method or property |
Purpose |
|
Public property that gets the number of elements in the |
|
The indexer for the |
|
Public property that gets a collection containing the keys in the |
|
Public property that gets a collection containing the values in the |
|
Adds an entry with a specified |
|
Removes all objects from the |
|
Determines whether the |
|
Determines whether the |
|
Returns an enumerator for the |
|
Implements |
|
Removes the entry with the specified |
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
.
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.
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.
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)