A class-based approach with BetterArray
In 2013, Ioannis commented that you could create a class to manage array resizing in chunks. An implementation with many other features—sorting, slicing, filtering, and conversion, to name a few—can now be found here: https://senipah.github.io/VBA-Better-Array/ (I am not connected to the project). A page on its capacity property explains the internal doubling process. More generally:
Stored in a single .cls file, the BetterArray class can easily be
imported into any new or existing VBA project. It’s written in pure
VBA and doesn’t use any external dependencies. As such, it should work
in any application that supports VBA scripting across both Windows and
Mac operating systems.
In other words, you simply download the class module (a single .cls file, linked here), drag it into your project*, and from there it’s available like a collection or any other object to create. Here I use it to get the contents of the current directory:
Sub DemoBetterArray()
Dim ba As BetterArray, tempDir As String, basicArray As Variant
Set ba = New BetterArray
tempDir = Dir("")
Do While tempDir <> ""
ba.Push tempDir 'I set no bounds, but I'm adding an element
tempDir = Dir()
Loop
basicArray = ba.Items 'See results in a traditional array
End Sub
For this example, you could do similar with an ArrayList
, generally available on Windows via .NET (but apparently obsolete in .NET). See a summary. In any case, there are major differences between these objects you could explore. For adding 1,000,000 integers, I found BetterArray to be several times faster than an ArrayList.
A tip for using the BetterArray documentation: While the page on examples is currently blank, the pages on methods (listed here) give many helpful examples of what the class can do in addition to expanding efficiently.
Expansion via Error Handling
Another possibility not yet discussed is to use error handling. The approach is demonstrated by Bruce McKinney in Hardcore Visual Basic, 2nd Edition (1997). The function below uses this idea.
Sub VectorFill(sourceVector As Variant, Index As Long, Value As Variant)
'Fills a 1d array at a specified index and increases the UBound if needed (by doubling it).
'Trim unneeded space separately with ReDim Preserve.
Const resizeMultiplier As Integer = 2
'With this statement, an out of bounds error will trigger a resize
On Error GoTo ErrorHandling
sourceVector(Index) = Value
Exit Sub
'ErrorHandling used to resize array in chunks
ErrorHandling:
newBound = (UBound(sourceVector) + 1) * resizeMultiplier '+1 helps with initial 0
ReDim Preserve sourceVector(newBound)
Resume 'New space is available, so go back and try again
End Sub
The above function can be used as follows:
Sub DemoVectorFill()
Dim dirContents() As Variant, i As Long
ReDim dirContents(0)
dirContent = Dir("")
Do While dirContent <> ""
VectorFill dirContents, i, dirContent
dirContent = Dir
i = i + 1
Loop
ReDim Preserve dirContents(i - 1)
End Sub
With this approach, you don't have to check capacity at each iteration, and leverage the error when it occurs. On my tests it is not faster than making that check (it’s a hair slower), but as sizes increase either way is much faster than ReDim Preserve at each iteration.
*If you try to copy and paste the BetterArray code into a class module, this will not entirely work. Class modules have some code that is hidden in the VBA editor, and won't be copied via copy paste. The two options are to drag the .cls file into the Project pane or use File --> Import File.