RSS Feed
Download our iPhone app
Browse DevX
Sign up for e-mail newsletters from DevX


Use Specialty Arrays to Accelerate Your Code : Page 3

Learn how to build unusually shaped triangular and sparse arrays, and arrays with non-zero lower bounds that are much faster than those provided by the standard .NET Array class.

Non-Zero Lower Bounds
Once upon a time, Visual Basic allowed you to create arrays with non-zero lower bounds. That way, if you wanted to store decimal sales figures for the years 1991 through 2010, you could create an array with corresponding bounds like this:

   Dim sales(1991 To 2010) As Decimal
Unfortunately in Visual Studio .NET (both C# and Visual Basic), all arrays use zero for their lower bounds. (As a largely symbolic concession, Microsoft now allows you to explicitly declare the lower bound in Visual Basic as long as it's zero. For example, you can write: Dim people(0 To 100)—but you still can't have non-zero lower bounds.)

Although Visual Studio doesn't allow arrays with non-zero lower bounds, you can work around that restriction with a little simple subtraction.

For example, suppose you want to build an array of sales figures for the years 1991 through 2010. You can save the values in a zero-based array and subtract 1991 from the indexes you use to get and fetch values. For example, you would place the value for the year 2000 in array entry 2000—1991 = 9. The following code makes an array big enough to hold the values and then places the value 32,000 in the entry for 2000:

   Dim sales(2010 - 1991) As Decimal
   sales(2000 -- 1991) = 32000
Unfortunately the extra subtraction makes your code slightly harder to read. The following OneDArray class wraps up the details to protect those who are math phobic. Notice that the class is generic, so you can use it to make an array of strings, decimals, pickles, politicians, or whatever (error-checking code that verifies bounds and indexes omitted for brevity):

   Public Class OneDArray(Of T)
      Private m_LowerBound As Integer
      Private m_UpperBound As Integer
      Private m_Values() As T
      Public Sub New(ByVal lower_bound As Integer, _
         ByVal upper_bound As Integer)
         ' Save the bounds.
         m_LowerBound = lower_bound
         m_UpperBound = upper_bound
         ' Create the storage array.
         ReDim m_Values(m_UpperBound - m_LowerBound)
      End Sub
      Default Public Property Item(ByVal index As Integer) As T
            Return m_Values(index - m_LowerBound)
         End Get
         Set(ByVal Value As T)
            m_Values(index - m_LowerBound) = Value
         End Set
      End Property
   End Class
The constructor saves the desired upper and lower bounds and makes a normal array to hold the values. The Item property subtracts the lower bound from the index to find the desired entry in the value array.

The Default keyword added to the property definition makes this the default property (the class indexer in C#) for the class. Again, that means a program can treat an object of the class as if it were an array and the "index" passed to the object is sent as a parameter to the property.

The following code shows the previous example rewritten to use the OneDArray class. Notice how the second line treats the sales object as if it were an array:

   Dim sales As New OneDArray(Of Integer)(1991, 2010)
   sales(2000) = 32000
This idea is easy to generalize for higher-dimensional arrays. You simply save the bounds for all dimensions and subtract them from the indexes used to get and set values in the array. The following code shows the key parts of the TwoDArray class, which implements a two-dimensional array with non-zero lower bounds:

   Public Class TwoDArray(Of T)
      Private m_LowerBound0 As Integer
      Private m_LowerBound1 As Integer
      Private m_UpperBound0 As Integer
      Private m_UpperBound1 As Integer
      Private m_Values(,) As T
      Public Sub New( _
       ByVal lower_bound0 As Integer, _
       ByVal upper_bound0 As Integer, _
       ByVal lower_bound1 As Integer, _
       ByVal upper_bound1 As Integer)
         ' Save the bounds.
         m_LowerBound0 = lower_bound0
         m_LowerBound1 = lower_bound1
         m_UpperBound0 = upper_bound0
         m_UpperBound1 = upper_bound1
         ' Create the two-dimensional storage array.
         ReDim m_Values( _
            m_UpperBound0 - m_LowerBound0, _
            m_UpperBound1 - m_LowerBound1)
      End Sub
      Default Public Property Item( _
       ByVal index0 As Integer, ByVal index1 As Integer) As T
            Return m_Values(index0 - m_LowerBound0, _
               index1 - m_LowerBound1)
         End Get
         Set(ByVal Value As T)
            m_Values(index0 - m_LowerBound0, _
               index1 - m_LowerBound1) = Value
         End Set
      End Property
   End Class
As a usage example, suppose you want to save sales figures for each of the four quarters in the years 1991 through 2010. The following code creates a TwoDArray object to represent the year in its first dimension and the fiscal quarter in the second. It then sets the third quarter sales figure for the year 2000 to 8,000:

   Dim sales As New TwoDArray(Of Decimal)(1991, 2010, 1, 4)
   sales(2000, 3) = 8000
You can extend this idea in a similar manner to accommodate three, four, or more dimensions. Unfortunately, subtraction alone isn't enough to make a single general class that can handle any number of dimensions the way the Array class can. To do that, you need to use some higher mathematics: multiplication and addition.

Close Icon
Thanks for your registration, follow us on our social networks to keep up-to-date