Let’s talk for a bit about how dynamic arrays work “behind the scenes.” First, a dynamic array primer. If you’re already comfortable with the use of dynamic arrays, you can skip this section:

In the old days, when you needed an array in your Delphi program, the array had to be static, which meant its size had to be known at the time you wrote your code. Those look like this:

var Nums: array[0..8] of Integer;

Way back in Delphi 4, dynamic arrays were added to the language. Dynamic arrays can vary in size. You don’t need to declare its size when you write your code, and you can programatically change the size of the array. Dynamic arrays look like this:

var Nums: array of Integer;

This creates a reference to the array, but doesn’t actually allocate any memory for the contents of the array. Before we can put something in the array, we need to set its length using the conveniently named SetLength procedure:

SetLength(Nums, 8);

Now that the length is set, we can assign values to the array, just like we could with a static array:

Nums[0] := 1; Nums[1] := 2; Nums[2] := 4; {...} Nums[7] := 128;

(Note that dynamic arrays are zero-indexed, so the highest index in the array is Length – 1)

What happens if, during the course of our program running, the size of the array needs to change? With static arrays, this simply wasn’t possible. With dynamic arrays, it’s easy! Just call SetLength again, then you can use the newly allocated elements in your array:

SetLength(Nums, 16); Nums[8] := 256; {...} Nums[15] := 32768;

Even after you’ve changed the size, the original elements are still there – no data is lost (OK, data *IS* lost if you resize an array to be SMALLER than it was before), and you just continue on your way.

So, how does all this magic that makes java developers jealous happen under the hood?

Static arrays are really just pointers to a contiguous area of memory that was allocated to hold the right number of elements for the array. The compiler does the math to turn an array index into a pointer to the into a pointer to a single element in the array.

Dynamic arrays, on the other hand, add a level of indirection. The dynamic array is really a record that holds a few pieces of information about the array, including its size (number of elements), and a pointer to a static array! When you call SetLength, several things happen:

The size field is updated A new (internal) static array is allocated with the new size The elements of the old static array are copied to the new array The static array pointer is changed to the new array The old static array’s memory is disposed

OK, there’s technically a bit more to it than that. Two points:

If there is enough room at the internal static array’s current position to reallocate, AND there is only one variable referencing the array, only step #1 happens. It is, however, good practice to assume that all of this happens every time you change the array’s length.

there is enough room at the internal static array’s current position to reallocate, AND there is only one variable referencing the array, only step #1 happens. It is, however, good practice to assume that all of this happens every time you change the array’s length. If there are are other variables that refer to the same array, the old static array will not be disposed, because the other variables will continue to point to the old data.

Even with those caveats, for the remainder of this discussion, we’ll assume that all of these steps happen with every change of the dynamic array’s length.

To get the best performance out of your Delphi program, it’s really important to understand the implications of step #3. Whenever you change the size of a dynamic array, the array will be copied, which means iterating through the array in memory. The time this takes is proportional to the size of the array: It takes longer to copy an array of 20 elements than an array of 10.

So what are we to do? We shouldn’t shy away from using dynamic arrays – they’re very useful! We just need to understand best practices for using them. Those best practices depend on the use case.

Here are two common use cases, along with some tips for getting the best dynamic array performance out of them:

First, if you’re reusing a dynamic array variable, but don’t need the old contents after resizing, do the following:

Don’t maintain more than one reference to the array. In other words, if A and B are dynamic arrays, never write A := B;

Before using SetLength to resize the array, call SetLength(arr, 0); This “resets” the array, and since the new array size is 0, doesn’t copy anything

This “resets” the array, and since the new array size is 0, doesn’t copy anything After calling SetLength(arr, 0) , call SetLength again to set your dynamic array to its new size

The effects of this are negligible for small arrays, but if you’re using large arrays (how large is hardware-dependent), this will yield significant speed increases. In one of my tests, using this technique shaved almost 10 milliseconds off of the speed of an array resize.

The second case is if your array is going to “live” for a long time, growing continuously. This is typical of any kind of aggregation scenario: an array of strings for a log, and array of numbers being collected for later analysis, etc….

Choose a “reasonable” initial length for your array. Make your best guess as to how many elements it might need to hold in a typical situation.

NEVER increase the length of your array by 1, or 2, or any constant amount. Never do anything that looks like this: SetLength(arr, Length(arr) + 1);

When the time comes to reallocate the array, double its size: SetLength(arr, Length(arr) * 2) . Research shows that this strikes the ideal balance between time and space for most cases.

. Research shows that this strikes the ideal balance between time and space for most cases. Consider using a TList. TList isn’t always the most efficient way to handle a “continuous growth” data structure, but its ease of use might make up for it. For very small arrays (less than a couple hundred element), TList will be just as fast as using a dynamic array with the size-doubling technique, but for anything larger, the dynamic array will be faster.

In a nutshell: dynamic arrays are very useful, precisely because they can be resized, but understanding how to best resize them is important for getting the best speed and memory performance out of your applications.

Take your Delphi programming to the next level with Castalia for Delphi, an advanced code editor for Delphi, integrated right into your favorite IDE.