Visual Basic .NET provides two basic kinds of arrays. First, it provides the normal arrays that you get when you declare a variable by using parentheses. For example, the following code declares an array of Integers named squares. The array contains 11 items with indexes ranging from 0 to 10. The code loops over the items, setting each one’s value. Next, it loops over the values again, adding them to a string. When it has finished building the string, the program displays the result.
Private Sub ShowSquares() Dim squares(10) As Integer For i As Integer = 0 To 10 squares(i) = i * i Next i Dim txt As String = "" For i As Integer = 0 To 10 txt &= squares(i).ToString & vbCrLf Next i MessageBox.Show(txt) End Sub
The Visual Basic Array class provides another kind of array. This kind of array is actually an object that provides methods for managing the items stored in the array.
The following code shows the previous version of the code rewritten to use an Array object. This version creates the array by using the Array class’s shared CreateInstance method, passing it the data type that the array should contain and the number of items that it should hold. The code then loops over the items using the array’s SetValue method to set the items’ values. If you have Option Strict turned off, the code can set the items’ values exactly as before by using the statement squares(i) =i*i. If Option Strict is on, you need to use SetValue. Next, the program loops over the items again, using the array’s GetValue method to add the item values to a string. If Option Strict is off, you can use the same syntax as before: txt &= squares(i).ToString & vbCrLf. If Option Strict is on, you need to use the array’s GetValue method. After building the string, the program displays it in a message box as before.
Private Sub ShowSquares() Dim squares As Array = _ Array.CreateInstance(GetType(Integer), 11) For i As Integer = 0 To 10 squares.SetValue(i * i, i) Next i Dim txt As String = "" For i As Integer = 0 To 10 txt &= squares.GetValue(i).ToString & vbCrLf Next i MessageBox.Show(txt) End Sub
The following sections describe the similarities and differences between normal arrays and Array objects.
Both normal variable arrays and Array objects can support multiple dimensions. The following statement declares a three-dimensional array with 11 items in the first dimension, 11 in the second, and 21 in the third. It then sets the value for the item in position (1, 2, 3).
Dim values(10, 10, 20) As Integer values(1, 2, 3) = 100
The following code does the same thing with an Array object:
Dim values As Array = _ Array.CreateInstance(GetType(Integer), 11, 21, 31) values.SetValue(100, 1, 2, 3)
If Option Strict is off, the code can use the same syntax for getting and setting the Array item’s value.
The following code sets the (1, 2, 3) item’s value to 100 and then displays its value:
Option Explicit Off ... values(1, 2, 3) = 100 Debug.WriteLine(values(1, 2, 3))
A normal array of variables always has lower bound 0 in every dimension. The following code declares an array with indexes ranging from 0 to 10:
Dim values(10) As Integer
You can fake a variable array that has nonzero lower bounds, but it requires extra work on your part. You must add or subtract an appropriate amount from each index to map the indexes you want to use to zero-based indexes.
The following code fragment uses an index variable i that ranges from 100 to 110. The code subtracts 100 from each index to map the values 100 to 110 to the values 0 to 10. If you wanted a multi-dimensional array with nonzero lower bounds, you would need to add or subtract from the indexes in each dimension.
For i As Integer = 100 To 110 values(i - 100) = i Next i
Array objects can handle nonzero lower bounds for you. The following code creates a two-dimensional array with indexes ranging from 1 to 10 in the first dimension, and 101 to 120 in the second dimension. First, it defines an array containing the lower bounds it wants to use for each dimension. Next, it defines an array containing the number of elements it wants for each dimension.
The code then calls the Array class’s shared CreateInstance method, passing it the data type of the array’s objects, the array of dimension lengths, and the array of lower bounds. The CreateInstance method uses the arrays of lower bounds and dimensions to create an Array object with the appropriate bounds.
Dim lower_bounds() As Integer = {1, 101} Dim dimension_lengths() As Integer = {10, 20} Dim values As Array = _ Array.CreateInstance(GetType(Integer), dimension_lengths, lower_bounds)
The following code sets the values in this array:
For i As Integer = 1 To 10 For j As Integer = 101 To 120 values.SetValue(i * 100 + j, i, j) Next j Next i
If Option Explicit is off, the program can use the following simpler syntax:
For i As Integer = 1 To 10 For j As Integer = 101 To 120 values(i, j) = i * 100 + j Next j Next i
You can use the ReDim statement to change a normal array’s dimensions. Add the Preserve keyword if you want the array to keep its existing values, as shown here:
Dim values(100) As Integer ... ReDim Preserve values(200)
An Array object cannot resize itself, but it is relatively easy to copy an Array object’s items to another Array object. The following code creates a values array containing 101 items with indexes ranging from 0 to 100. Later, it creates a new Array object containing 201 items and uses the values array’s CopyTo method to copy its values into the new array. The second parameter to CopyTo gives the index in the destination array where the copy should start placing values.
Dim values As Array = _ Array.CreateInstance(GetType(Integer), 101) ... Dim new_array As Array = _ Array.CreateInstance(GetType(Integer), 201) values.CopyTo(new_array, 0) values = new_array
The Array class’s shared Copy method allows you greater control. It lets you specify the index in the source array where the copy should start, the index in the destination array where the items should be copied, and the number of items to be copied.
Although building a new Array object and copying items into it is more cumbersome than using ReDim to resize a variable array, the process is surprisingly fast.
There’s no doubt that arrays of variables are much faster than Array objects. In one test, setting and getting values in an Array object took more than 100 times as long as performing the same operations in a variable array.
If your application performs only a few hundred or a thousand array operations, the difference is unimportant. If your application must access array values many millions of times, you may need to consider using an array of variables even if the Array class would be more convenient for other reasons (such as nonzero lower bounds).
Microsoft has also optimized one-dimensional variable arrays, so they are faster than multidimensional arrays. The difference is much less dramatic than the difference between variable arrays and Array classes, however.
The following code compares the speeds of a one-dimensional array and a two-dimensional array. After some setup, it creates a one-dimensional array, fills it with values, and displays the elapsed time.
Next, the program erases the first array to free its memory. If you don’t do this, then the second array might use up all of the system’s memory and force the program to swap data to the disk. That greatly slows the program and makes the two-dimensional array seem even slower than it really is.
The program then creates the two-dimensional array, fills it with values, and displays the elapsed time. It repeats these steps for one- and two-dimensional Array objects as well.
Private Sub btnGo_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles btnGo.Click Dim start_time As DateTime Dim stop_time As DateTime Dim elapsed_time As TimeSpan ' Clear any previous results. lbl1D.Text = "" lbl2D.Text = "" lbl1Dclass.Text = "" lbl2Dclass.Text = "" Cursor = Cursors.WaitCursor Refresh() ' Get the number of items. Dim num_items As Integer = Integer.Parse(txtNumItems.Text) ' One-dimensional array. Dim array1(0 To num_items - 1) As Integer start_time = Now For i As Integer = 0 To num_items - 1 array1(i) = i Next i stop_time = Now elapsed_time = stop_time.Subtract(start_time) lbl1D.Text = elapsed_time.TotalSeconds().ToString lbl1D.Refresh() ' Free the first array's memory. Erase array1 ' Two-dimensional array. Dim array2(0 To 0, 0 To num_items - 1) As Integer start_time = Now For i As Integer = 0 To num_items - 1 array2(0, i) = i Next i stop_time = Now elapsed_time = stop_time.Subtract(start_time) lbl2D.Text = elapsed_time.TotalSeconds().ToString ' Free the second array's memory. Erase array2 ' One-dimensional Array. Dim array3 As Array = _ Array.CreateInstance(GetType(Integer), num_items) start_time = Now For i As Integer = 0 To num_items - 1 array3.SetValue(i, i) Next i stop_time = Now elapsed_time = stop_time.Subtract(start_time) lbl1Dclass.Text = elapsed_time.TotalSeconds().ToString lbl1Dclass.Refresh() ' Free the Array's memory. array3 = Nothing ' Two-dimensional Array. Dim array4 As Array = _ Array.CreateInstance(GetType(Integer), 1, num_items) start_time = Now For i As Integer = 0 To num_items - 1 array4.SetValue(i, 0, i) Next i stop_time = Now elapsed_time = stop_time.Subtract(start_time) lbl2Dclass.Text = elapsed_time.TotalSeconds().ToString Cursor = Cursors.Default End Sub
Figure 18-1 shows the results. Variable arrays are much faster than array classes. One-dimensional variable arrays generally seem to be slightly faster than two-dimensional arrays.
Figure 18-1: Variable arrays are faster than array classes.
The Array class provides several other useful shared methods that were not available in Visual Basic 6 and earlier versions. For example, the IndexOf and LastIndexOf methods return the position of a particular item in an Array object’s items. The following code creates an Array object and fills it with integers. It then displays the indexes of the first occurrence of the value 6 and the last occurrence of the value 3.
Dim values As Array = _ Array.CreateInstance(GetType(Integer), 11) For i As Integer = 0 To 10 values.SetValue(i, i) Next i MessageBox.Show(Array.IndexOf(values, 6).ToString) MessageBox.Show(Array.LastIndexOf(values, 3).ToString)
Methods such as IndexOf and LastIndexOf would be a strong argument supporting Array objects over normal arrays of variables if it weren’t for one somewhat surprising fact: Those same methods work with regular arrays of variables, too! The following code is similar to the previous example, except that it uses an array of variables instead of an Array object:
Dim values(10) As Integer For i As Integer = 0 To 10 values(i) = i Next i MessageBox.Show(Array.IndexOf(values, 6).ToString) MessageBox.Show(Array.LastIndexOf(values, 3).ToString)
The following sections describe some of the Array class’s other useful shared methods. All of these work both for arrays of variables and Array objects.
The Array.Reverse method reverses the order of the items in an array. The following code demonstrates this method. It builds and initializes an array of integers, calls Array.Reverse, and then displays the items in their new order.
Dim values(10) As Integer For i As Integer = 0 To 10 values(i) = i Next i Array.Reverse(values) Dim txt As String For i As Integer = 0 To 10 txt &= values(i) & " " Next i MessageBox.Show(txt)
There’s nothing particularly confusing about this method. It can easily reverse its items even if the items are not things that you can reasonably compare. For example, it can reverse an array of integers, strings, StockOption objects, or TreeView controls.
The Array.Sort method sorts the items in the array. To sort the items, this method must compare them to each other. That means the items must be things that can be reasonably compared (such as integers, strings, or dates). More precisely, the method can sort the items if they implement the IComparable interface, meaning they contain the means to compare themselves to each other.
The following code shows a Person class that implements the IComparable interface. The class defines two public strings, FirstName and LastName. For convenience, it also defines a constructor and a ToString function. The code then defines the CompareTo function that is required by the IComparable interface. This function should compare the value of the current object (object1) to the value of the object passed as a parameter (object2) and return the following: -1 if object1 should come before object2, 0 if neither object must come before the other, and 1 if object2 should come before object1. The String.Compare function makes exactly that calculation for two strings, so the CompareTo function uses it to compare the two Person objects’ names. You could use a more complicated CompareTo function to order just about anything.
Public Class Person Implements IComparable Public FirstName As String Public LastName As String Public Sub New(ByVal first_name As String, ByVal last_name As String) FirstName = first_name LastName = last_name End Sub Public Overrides Function ToString() As String Return LastName & ", " & FirstName End Function Public Function CompareTo(ByVal obj As Object) As Integer _ Implements System.IComparable.CompareTo Dim other_Person As Person = DirectCast(obj, Person) Return String.Compare(Me.ToString, other_Person.ToString) End Function End Class
The following code makes and sorts an array of Person objects:
' Make the array of Person objects. Dim people(4) As Person people(0) = New Person("Rod", "Stephens") people(1) = New Person("Sergio", "Aragones") people(2) = New Person("Terry", "Pratchett") people(3) = New Person("Homer", "Simpson") people(4) = New Person("Eoin", "Colfer") ' Sort. Array.Sort(people) ' Display the results. Dim txt As String = "" For i As Integer = 0 To people.GetUpperBound(0) txt &= people(i).ToString() & vbCrLf Next i MessageBox.Show(txt)
You can also sort objects that do not implement the IComparable interface if you pass the Sort method an object that can sort them. The following code defines a Manager class that is similar to the Employee class without the IComparable interface. It also defines a ManagerComparer class that implements the IComparer interface. This class’s Compare method takes two objects as parameters and returns -1, 0, or 1 to indicate which should come before the other.
Public Class Manager Public FirstName As String Public LastName As String Public Sub New(ByVal first_name As String, ByVal last_name As String) FirstName = first_name LastName = last_name End Sub Public Overrides Function ToString() As String Return LastName & ", " & FirstName End Function End Class Public Class ManagerComparer Implements IComparer Public Function Compare(ByVal x As Object, ByVal y As Object) As Integer _ Implements System.Collections.IComparer.Compare Dim mgr1 As Manager = DirectCast(x, Manager) Dim mgr2 As Manager = DirectCast(y, Manager) Return String.Compare(mgr1.ToString, mgr2.ToString) End Function End Class
Tip | The call to DirectCast converts its parameters from one type to another. In this code, it converts the variables x and y into Manager objects. Note that DirectCast only works if the variables are of the new type. In this case, we know that variables x and y are Manager objects that have been passed to the Compare function as Object variables. If the variables have some other type that can be converted into the desired type, you would need to use CType instead of DirectCast. For example, suppose that x and y were Supervisor objects and Supervisor is a subclass of Manager. Then you could convert x and y into Manager objects because a Supervisor is a type of Manager, but you would need to use CType instead of DirectCast. |
The following code uses a ManagerComparer object to sort an array of Manager objects:
' Make the array of Manager objects. Dim managers(4) As Manager managers(0) = New Manager("Rod", "Stephens") managers(1) = New Manager("Sergio", "Aragones") managers(2) = New Manager("Terry", "Pratchett") managers(3) = New Manager("Homer", "Simpson") managers(4) = New Manager("Eoin", "Colfer") ' Make a comparer. Dim manager_comparer As New ManagerComparer ' Sort. Array.Sort(managers, manager_comparer) ' Display the results. Dim txt As String = "" For i As Integer = 0 To managers.GetUpperBound(0) txt &= managers(i).ToString() & vbCrLf Next i MessageBox.Show(txt)
Other overloaded versions of the Sort method let you sort two arrays (one containing keys and the other values) in tandem or sort only parts of the array.
If the array contains items that are sorted, and the items implement the IComparable interface, the Array.BinarySearch method uses a binary search algorithm to locate a specific item within the array.
For example, suppose that the array contains Person objects as defined in the previous section. Then you could use the following code to find the object representing Rod Stephens in the people array. The code starts by using Array.Sort to sort the array. If you know that the array is already sorted, the program can skip this step. Next the program makes a new Person object that has the name Rod Stephens. The program needs this object to represent the target. The program calls the ArrayBinarySearch method to find the index of the object with the target name and saves the result in the variable target_index. It then uses that index to assign the target_person variable to the corresponding Person object in the array.
' Sort the array. Array.Sort(people) ' Find Rod Stephens. Dim target_person As New Person("Rod", "Stephens") Dim target_index As Integer = Array.BinarySearch(people, target_person) target_person = people(target_index) MessageBox.Show(target_person.ToString)
Binary search is extremely fast. To understand why, you must understand how the algorithm works. The details are tricky, but the idea is straightforward. At each step, the program picks the item in the middle of the part of the array that it is considering and compares it to the target item. If the target comes before the middle item, then the algorithm repeats the search in the first half of the items. If the target comes after the middle item, the algorithm repeats the search in the second half of the items.
Figure 18-2 illustrates a binary search for the value 12. The program starts considering the whole array. It picks the item in the middle with value 9 and compares it to the target 12. The target value 12 is greater than 9, so the routine discards the items on the left and considers those with values 12 through 22.
Figure 18-2: Binary search quickly locates a target value in an array.
Next, the program picks the middle item from those remaining and compares it to the target. The middle value 17 is greater than 12, so the program discards the items to the right and considers the remaining items to the left. In this example, only one item remains and it has the target value 12.
In this example, a binary search needs to examine only three items to find the target value 12. More generally, each time it examines an item, the program removes half of the remaining items from consideration. If the array contains N items, then the algorithm only needs to repeat this process Log2(N) times before it has found the target or eliminated every item from consideration. For example, if the array contains 1,048,576 = 220 items, then the program will need to examine only around 20 items before it finds the target. That’s pretty fast! An array large enough to really slow binary search down would be so large that you probably couldn’t store it all in your computer’s memory at once anyway. For such huge arrays, you would be better off storing the data in a database and using a database engine to search it.