Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Should I use a "TList<String>" instead of "Array of String" always?

Tags:

delphi

I have been migrating a project developed in Delphi 7 and looking at the code everytime the previous developers wanted to use a collection they created something like this:

ArrayOfString : array of string;
ArrayOfInteger: array of integer;
ArrayOfStringArrays : array of ArrayOfString;

This sort of code is repeated in many places, and also the "SetLength" for each of them several times, i wonder if changing all this Array of Something to a TList<Something> now that we are in Delphi XE4.

Is there any advantage in doing this, resource, speed or something like that to back me up in this decision?

PS: I come from JAVA and Delphi to me feels dark and full of terrors

like image 202
Diego Rueda Avatar asked Jun 17 '14 22:06

Diego Rueda


2 Answers

Think of a dynamic array as being a lower level construct than TStringList or TList<T>.

The dynamic array offers direct access to elements via a pointer. The language hides the pointer from you, but that's essentially all a dynamic array is. But you are in charge of any reallocations and if you wish to insert or remove items, then you have to write the code, and deal with the details.

The higher level collection classes, TStringList and TList<T> are built on top of dynamic arrays. That is internally how the contents are stored. But the collection classes wrap that up for you. Higher level operations like inserting and deleting, and many more, are provided as methods. In essence, these collection classes provide a great deal more convenience than raw dynamic arrays.

To illustrate, consider the act of inserting an item. For a dynamic array you would do this:

  1. Resize the array.
  2. Move items that are after the insertion point from position i to i+1.
  3. Assign the inserted item.

If you need to write this code more than once, well, you are doing it wrong.

For the high level collection you write:

List.Insert(Index, Value);

and let the class take care of the details.

Note that for historical reasons, and because strings are especially important, developers tend to use the bespoke, dedicated TStringList class rather than TList<string>. Again the dedicated class offers functionality above and beyond TList<string> because it specialises in strings and can offer functionality that is bespoke to strings. Again, the dedicated class offers convenience.

One place where dynamic arrays come in handy is when you don't want to incur the boilerplate of lifetime management. So for the desktop compilers that don't have ARC for classes, you need to explicitly destroy TList<T> instances. But dynamic arrays have lifetime managed by ARC. If you are synthesising arrays in one shot, and then not resizing them, then the lifetime management issue can make arrays more convenient to use.

As a rule of thumb, prefer the high level collection classes. They should be your default choice. Sometimes dynamic arrays are the right choice, but that tends to be for more specialised scenarios.

like image 185
David Heffernan Avatar answered Oct 19 '22 16:10

David Heffernan


First, in newer versions of Delphi, there's a TArray<T> type that can be used to replace all those old array of whatever declarations. Using it can work around some long-standing gotchas in the language, left over from classic Pascal, and help you not run into confusing bugs down the road.

Having said that, TStringList is not a string array; it's a container object that holds a list of strings. It has several specialized methods for string-list handling that make it extremely versatile: it can essentially be used as a list of strings, a set of strings (via the .Sorted property), a string/object map (via the .Objects property) and a string/string map (via the .Names and .Values properties.)

If you find you're calling SetLength on an array a lot, especially if it's anything like the following, you should definitely convert it to a list class:

for i := 0 to Count - 1 do
begin
   SetLength(myArray, length(myArray) + 1);
   myArray[high(myArray)] := values[i];
end;

With a generic TList<T> or a TStringList, this becomes:

for i := 0 to Count - 1 do
begin
   myList.Add(values[i]);
end;

This is both simpler to read and significantly more performant (especially with a large Count value) because the list classes are able to track their size internally and keep the memory reallocations down. So list classes are a win-win situation most of the time. There are some scenarios where you would prefer dynamic arrays, but they're kind of rare.

One thing to be aware of, though: If you're from a Java background, keep in mind that dynamic arrays are compiler-managed types, but list classes are objects that need to be freed in code.

like image 39
Mason Wheeler Avatar answered Oct 19 '22 15:10

Mason Wheeler