The Array class is the simplest of the collection types provided with the .NET Framework. But the Framework provides a number of more powerful collection classes. The remaining pages of this chapter describe some of these very useful collection classes: ArrayList, Collection, Queue, and Stack.
Imagine that your program asks the user for input or gathers input from a web site. As it finds objects (strings, books, values, etc.), you would like to add them to an array, but you have no idea how many objects you'll collect in any given session.
It is difficult to use an array for such a purpose because you must declare the size of an Array object at compile time. If you try to add more objects than you've allocated memory for, the Array class will throw an exception. If you do not know in advance how many objects your array will be required to 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).
The .NET Framework provides a class designed for just this situation. The ArrayList class is an array whose size is dynamically increased as required. The ArrayList class provides many useful methods and properties. A few of the most important are shown in Table 9-4.
Method or property |
Purpose |
---|---|
Method to add an object to the ArrayList |
|
Property containing the number of elements the array can currently hold |
|
Method that removes all elements from the ArrayList |
|
Property to return the number of elements currently in the array |
|
Method that returns an enumerator to iterate an ArrayList |
|
Method that inserts an element into ArrayList |
|
Method that gets or sets the element at the specified index; this is the indexer for the ArrayList class |
|
Method that removes the element at the specified index |
|
Method that reverses the order of elements in the ArrayList |
|
Method that alphabetically sorts the ArrayList |
|
Method that copies the elements of the ArrayList to a new array |
When you create an ArrayList, you do not define how many objects it will contain. You add to the ArrayList using the Add( ) method, and the list takes care of its own internal bookkeeping, as illustrated in Example 9-13.
Option Strict On Imports System Namespace ArrayListDemo 'a class to hold in the array list Public Class Employee Private myEmpID As Integer Public Sub New(ByVal empID As Integer) Me.myEmpID = empID End Sub 'New Public Overrides Function ToString( ) As String Return myEmpID.ToString( ) End Function 'ToString Public Property EmpID( ) As Integer Get Return myEmpID End Get Set(ByVal Value As Integer) myEmpID = Value End Set End Property End Class 'Employee Class Tester Public Sub Run( ) Dim empArray As New ArrayList( ) Dim intArray As New ArrayList( ) 'populate the arraylists Dim i As Integer For i = 0 To 4 empArray.Add(New Employee(i + 100)) intArray.Add((i * 5)) Next i 'print each member of the array For Each i In intArray Console.Write("{0} ", i.ToString( )) Next i Console.WriteLine(ControlChars.Lf) 'print each employee Dim e As Employee For Each e In empArray Console.Write("{0} ", e.ToString( )) Next e Console.WriteLine(ControlChars.Lf) Console.WriteLine("empArray.Capacity: {0}", empArray.Capacity) End Sub 'Run Shared Sub Main( ) Dim t As New Tester( ) t.Run( ) End Sub 'Main End Class 'Tester End Namespace 'ArrayListDemo Output: 0 5 10 15 20 100 101 102 103 104 empArray.Capacity: 16
Suppose you're defining two ArrayList objects, empArray to hold Employee objects, and intArray to hold integers:
Dim empArray As New ArrayList( ) Dim intArray As New ArrayList( )
Each ArrayList object has a property, Capacity, which is the number of elements the ArrayList is capable of storing.
|
You add elements to the ArrayList with the Add( ) method:
empArray.Add(New Employee(i + 100)) intArray.Add((i * 5))
When you add the 17th element, the capacity is automatically doubled to 32. If you change the For loop to:
For i = 0 To 17
the output looks like this:
0 5 10 15 20 25 30 35 40 45 50 55 60 65 70 75 80 85 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 empArray.Capacity: 32
Similarly, if you added a 33rd element, the capacity would be doubled to 64. The 65th element increases the capacity to 128, the 129th element increases it to 256, and so forth.
Visual Basic .NET offers a generic collection class named, aptly, Collection. In many ways, the Collection object serves as an object-oriented alternative to Array, much as ArrayList does.
These two constructs (ArrayList and Collection) are very similar. Both offer Add( ) and Remove( ) methods as well as an Item property. The Collection class, however, overloads the Item property to take a string as a key into the collection. This allows the Collection class to act as a dictionary, associating keys with values. You can also use the Item property to access members of the collection by index value; however, the Collection uses a one-based index (i.e., the first element is index 1 rather than 0).
Example 9-14 illustrates the use of a Visual Basic .NET Collection object.
Option Strict On Imports System Namespace CollectionDemo 'a class to hold in the array list Public Class Employee Private myEmpID As Integer Public Sub New(ByVal empID As Integer) Me.myEmpID = empID End Sub 'New Public Overrides Function ToString( ) As String Return myEmpID.ToString( ) End Function 'ToString Public Property EmpID( ) As Integer Get Return myEmpID End Get Set(ByVal Value As Integer) myEmpID = Value End Set End Property End Class 'Employee Class Tester Public Sub Run( ) Dim intCollection As New Collection( ) Dim empCollection As New Collection( ) Dim empCollection2 As New Collection( ) 'populate the Collections Dim i As Integer For i = 0 To 4 empCollection.Add(New Employee(i + 100)) intCollection.Add((i * 5)) Next i 'add key/value pairs empCollection2.Add(New Employee(1789), "George Washington") empCollection2.Add(New Employee(1797), "John Adams") empCollection2.Add(New Employee(1801), "Thomas Jefferson") 'print each member of the array For Each i In intCollection Console.Write("{0} ", i.ToString( )) Next i Console.WriteLine( ) Console.WriteLine("Employee collection...") Dim e As Employee For Each e In empCollection Console.Write("{0} ", e.ToString( )) Next e Console.WriteLine( ) Console.WriteLine("Employee collection 2...") For Each e In empCollection2 Console.Write("{0} ", e.ToString( )) Next e Console.WriteLine( ) 'retrieve an Employee by key Dim emp As Employee emp = empCollection2.Item("John Adams") Console.WriteLine( _ "Key John Adams retrieved empID {0}", emp.ToString( )) 'note that indexing is 1-based (rather than zero based) emp = empCollection2.Item(1) Console.WriteLine( _ "Index(1) retrieved empID {0}", emp.ToString( )) End Sub 'Run Shared Sub Main( ) Dim t As New Tester( ) t.Run( ) End Sub 'Main End Class 'Tester End Namespace 'CollectionDemo Output: 0 5 10 15 20 Employee collection... 100 101 102 103 104 Employee collection 2... 1789 1797 1801 Key John Adams retrieved empID 1797 Index(1) retrieved empID 1789
Example 9-14 creates three Collection objects (intCollection, empCollection, and empCollection2):
Dim intCollection As New Collection( ) Dim empCollection As New Collection( ) Dim empCollection2 As New Collection( )
The first two objects are populated in For loops, just as the ArrayList was created in Example 9-13:
Dim i As Integer For i = 0 To 4 empCollection.Add(New Employee(i + 100)) intCollection.Add((i * 5)) Next i
The third Collection object, empCollection2, is populated using key values. Each new Employee is associated with a string, representing the name of the Employee:
empCollection2.Add(New Employee(1789), "George Washington") empCollection2.Add(New Employee(1797), "John Adams") empCollection2.Add(New Employee(1801), "Thomas Jefferson")
You retrieve objects from the collection much as you did from the ArrayLists:
For Each i In intCollection Console.Write("{0} ", i.ToString( )) Next i Dim e As Employee For Each e In empCollection Console.Write("{0} ", e.ToString( )) Next e For Each e In empCollection2 Console.Write("{0} ", e.ToString( )) Next e
You can, however, retrieve objects from the collection using either the key value or an index value (one-based):
Dim emp As Employee emp = empCollection2.Item("John Adams") Console.WriteLine("Key John Adams retrieved empID {0}", emp.ToString( )) emp = empCollection2.Item(1) Console.WriteLine("Index(1) retrieved empID {0}", emp.ToString( ))
A queue represents a first-in, first-out (FIFO) collection. The classic analogy is 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.
The Queue class 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 handle only 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, the most important of which are shown in Table 9-5.
Method or property |
Purpose |
---|---|
Public property that gets the number of elements in the Queue |
|
Method that removes all objects from the Queue |
|
Method that determines if an element is in the Queue |
|
Method that copies the Queue elements to an existing one-dimensional array |
|
Dequeue( ) |
Method that removes and returns the object at the beginning of the Queue |
Enqueue( ) |
Method that adds an object to the end of the Queue |
Method that returns an enumerator for the Queue |
|
Method that returns the object at the beginning of the Queue without removing it |
|
Method that copies the elements to a new array |
You add elements to your queue with the Enqueue( ) method, and you take them off the queue with Dequeue( ) or by using an enumerator. Example 9-15 shows how to use a Queue, followed by the output and a complete analysis.
Option Strict On Imports System Namespace QueueDemo Class Tester Public Sub Run( ) Dim intQueue As New Queue( ) 'populate the array Dim i As Integer For i = 0 To 4 intQueue.Enqueue((i * 5)) Next i 'display the Queue Console.WriteLine("intQueue values:") DisplayValues(intQueue) 'remove an element from the Queue Console.WriteLine("(Dequeue) {0}", intQueue.Dequeue( )) 'display the Queue Console.WriteLine("intQueue values:") DisplayValues(intQueue) 'remove another element from the Queue Console.WriteLine("(Dequeue) {0}", intQueue.Dequeue( )) 'display the Queue Console.WriteLine("intQueue values:") DisplayValues(intQueue) 'view the first element in the Queue but do not remove Console.WriteLine("(Peek) {0}", intQueue.Peek( )) 'display the Queue Console.WriteLine("intQueue values:") DisplayValues(intQueue) End Sub 'Run Public Shared Sub DisplayValues(ByVal myCollection As IEnumerable) Dim myEnumerator As IEnumerator = myCollection.GetEnumerator( ) While myEnumerator.MoveNext( ) Console.WriteLine("{0} ", myEnumerator.Current) End While Console.WriteLine( ) End Sub 'DisplayValues Shared Sub Main( ) Dim t As New Tester( ) t.Run( ) End Sub 'Main End Class 'Tester End Namespace 'QueueDemo Output: intQueue values: 0 5 10 15 20 (Dequeue) 0 intQueue values: 5 10 15 20 (Dequeue) 5 intQueue values: 10 15 20 (Peek) 10 intQueue values: 10 15 20
In Example 9-15, the ArrayList from Example 9-13 is replaced by a Queue. I've dispensed with the Employee class and enqueued integers to save room in the book, but of course you can enqueue user-defined objects as well.
The program begins by creating an instance of a Queue, called intQueue:
Dim intQueue As New Queue( )
The queue is populated with integers:
For i = 0 To 4 intQueue.Enqueue((i * 5)) Next i
The contents of the queue are then displayed using the DisplayValues( ) method. This method takes a collection that implements the IEnumerable interface (as does each of the collections provided by the .NET Framework) and asks that collection for its enumerator. It then explicitly iterates over the collection, displaying each element in turn:
Public Shared Sub DisplayValues(ByVal myCollection As IEnumerable) Dim myEnumerator As IEnumerator = myCollection.GetEnumerator( ) While myEnumerator.MoveNext( ) Console.Write("{0} ", myEnumerator.Current) End While Console.WriteLine( ) End Sub 'DisplayValues
You can avoid all the details of the Enumerator by using the For Each loop instead:
Public Shared Sub DisplayValues( _ ByVal myCollection As IEnumerable) Dim o As Object For Each o In myCollection Console.WriteLine(o) Next End Sub 'DisplayValues
Either version of DisplayValues( ) will work equally well.
You can display the first value in the queue without removing it by calling the Peek( ) method:
Console.WriteLine("(Peek) {0}", intQueue.Peek( ))
Or, having displayed the values in the For Each loop, you can remove the current value by calling the Dequeue( ) method:
Console.WriteLine("(Dequeue) {0}", intQueue.Dequeue( ))
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. You add a dish on top, and it is the first dish you take off the stack.
|
The principal methods for adding to and removing from an instance of the Stack class are Push( ) and Pop( ); Stack also offers a Peek( ) method, very much like Queue. Table 9-6 shows the most important methods and properties for Stack.
Method or property |
Purpose |
---|---|
Method that removes all objects from the Stack |
|
Method that determines if an element is in the Stack |
|
Method that copies the Stack elements to an existing one-dimensional array |
|
Public property that gets the number of elements in the Stack |
|
Method that returns an enumerator for the Stack |
|
Method that returns the object at the top of the Stack without removing it |
|
Pop( ) |
Method that removes and returns the object at the top of the Stack |
Push( ) |
Method that inserts an object at the top of the Stack |
Method that copies the elements to a new array |
In Example 9-16, you rewrite Example 9-15 to use a Stack rather than a Queue. The logic is almost identical. The key difference is that a Stack is Last In, First Out, while a Queue is First In, First Out.
Option Strict On Imports System Namespace StackDemo Class Tester Public Sub Run( ) Dim intStack As New Stack( ) 'populate the stack Dim i As Integer For i = 0 To 7 intStack.Push((i * 5)) Next i 'display the Stack Console.WriteLine("intStack values:") DisplayValues(intStack) 'remove an element from the stack Console.WriteLine("(Pop){0}", intStack.Pop( )) 'display the Stack Console.WriteLine("intStack values:") DisplayValues(intStack) 'remove another element from the stack Console.WriteLine("(Pop){0}", intStack.Pop( )) 'display the Stack Console.WriteLine("intStack values:") DisplayValues(intStack) 'view the first element in the ' Stack but do not remove Console.WriteLine("(Peek) {0}", intStack.Peek( )) 'display the Stack Console.WriteLine("intStack values:") DisplayValues(intStack) End Sub 'Run Public Shared Sub DisplayValues(ByVal myCollection As IEnumerable) Dim o As Object For Each o In myCollection Console.WriteLine(o) Next o End Sub 'DisplayValues Shared Sub Main( ) Dim t As New Tester( ) t.Run( ) End Sub 'Main End Class 'Tester End Namespace 'StackDemo Output: intStack values: 35 30 25 20 15 10 5 0 (Pop)35 intStack values: 30 25 20 15 10 5 0 (Pop)30 intStack values: 25 20 15 10 5 0 (Peek) 25 intStack values: 25 20 15 10 5 0
You start Example 9-16 by creating a Stack object called intStack:
Dim intStack As New Stack( )
You populate the stack with integers by calling the Push( ) method, which pushes each integer object onto the stack (i.e., adds it to the top of the Stack):
For i = 0 To 7 intStack.Push((i * 5)) Next i
You remove an object from the stack by popping it off the stack with the Pop( ) method:
Console.WriteLine("(Pop){0}", intStack.Pop( ))
Just as you could peek at the object at the beginning of the Queue without dequeing it, you can Peek( ) at the object on top of the stack without popping it:
Console.WriteLine("(Peek) {0}", intStack.Peek( ))
Top |