Since Delphi got generic types, in the 2009 version, its runtime library (RTL) has a generic collections unit. The RTL however, still has the classic collections. Recently I got some requests about which is recommended to use, and while the discussion can go a bit deeper, I wanted to blog a high level overview.
Classic Container Classes
The most basic collection class in the RTL is the TList class (in the System.Classes unit), which represents a general list of pointers (or object references). The container classes (in the System.Contnrs unit) includes the following:
- TObjectList, which is a TList for TObject references and includes ownership support
- TComponentList, which adds to the ownership also the notification support all components have
- TStack and TQueue with TObjectStack and TObjectQueue
- Various bucket lists
When you are using these containers you often need to cast from the type of the objects you are managing to the fixed type the list supports. This introduced the possibility of errors and causes some runtime delay if you continuously check the type of the objects extracted from the list with dynamic casts (like “as”):
myList: TList; myList.Get(3) as TMyObject
Generic Collection Classes
Along with generic types in the language Delphi added a set of basic generic collections. These are defined in the System.Generics.Collections unit and offer a generic blueprint for containers tied to the specific data type you need. Generic collections include:
- TList<T>, a basic generic list including support for sorting and enumerations
- TThreadList<T>, a more thread-safe list with locking support
- TQueue<T> and TStack<T>
- TDictionary<TKey,TValue>, a fairly powerful dictionary with customizable key and value types
- TObjectList<T: class> which has ownership support for can be used only for object types, similarly to the other following containers
- TObjectQueue<T: class> and TObjectStack<T: class>
Advantages of Generic Collections
Generics collections offer the ability to define specific containers for given data types you need to use, and let the compiler check the data type compatibility, rather than doing the same at runtime. This results in cleaner and more readable code, more robust applications, and faster execution — given the reduced need ot runtime checks:
myList: TList <TMyObject>; myList.Get(3); // returns TMyObject
All of the new libraries and subsystems in Delphi use the new collections, for example FireMonkey leverages them a lot rather than using the traditional coding style.
Any Reason not to Use Generic Collections?
There are basically two reasons for not using the new collections. One is that if you have existing working code, you might not want to change it. Even more if the code is in a library (like VCL) changing it could cause incompatibility in code that uses the library.
The other reason is that generics in Delphi cause a significant code bloat, because for any data type used by the collection the class methods are replicated, even if almost identical. In very large applications, this has a negative effect on compile and link time and on executable side.
Additional Generic Collections
Finally, I want to mention that if you use generic collections heavily, you should consider those available in the Spring4D library, which extends what we have in the RTL. See the documentation at https://bitbucket.org/sglienke/spring4d/wiki/Collections
Again this is meant to be a short summary and I know I could have been more extensive and precise, hope it is helpful to some of the old developers not fully aware of our generic collections — which was the goal of the blog post.