One of my all time favorite objects in Delphi is TStringList which I have been using for over 20 years. Most apps I write with Delphi usually contain at least one usage of TStringList. I rarely use arrays as TStringList usually fills that space in my toolchest (the updated array syntax makes them tempting though). TFDMemTable is becoming the new TStringList for me though as now I rarely write an app that doesn’t use a TFDMemTable at least once. Yet TStringList is still use at least once as well!
TStringList is such a versatile object because it supports easily returning it’s contents as a string and saving/loading those same contents from a file. It easily handles key value pairs, splitting strings, and holding whatever string list (as the name implies) in memory. It handles sorting and even custom sorting. It can hold a list of objects that are tied to a string name and it can even free those objects automatically with the OwnsObjects property. It can ignore duplicates like an index database table. It can even be easily searched using IndexOf or Find.
Some of my favorite TStringList methods are TStringList.SaveToFile(), TStringList.LoadFromFile(), TStringList.SaveToStream(), TStringList.LoadFromStream(), TStringList.Append(), TStringList.AddObject(), TStringList.AddStrings(), and TStringList.IndexOfName(). While the properties I use most are probably TStringList.Text, TStringList.ValueFromIndex, and TStringList.Values. You can find a full breakdown of all of these methods and properties over in the Embarcadero DocWiki for TStringList.
The details are lost but I think I even used TStringList and the legendary FastStrings library at the core of a search engine index I built at the dawn of the new millennium with ~100,000 records. The results were returned in a second or two.
TFDMemTable Data IO
Lately I’ve been using TFDMemTable because it allows me to easily LiveBind bidirectional data to visual controls. TFDMemTable is a in memory database table that is part of the FireDAC (data access library) which is available in with FireMonkey in Delphi and C++Builder. TFDMemTable supports multiple columns (not just the 2 key value columns of TStringList), indexes, searching, exporting and importing from JSON, XML, binary, and even CSV format (through TFDBatchMove). TFDMemTable can be used as a visibility state machine for buttons through livebindings (see demo).
TFDMemTable is also the recipient of JSON that is automatically imported and consumed from TRESTResponse and TRESTResponseDatasetAdapter. The JSON can be imported directly, based on a specific root field name, or even flatted so all of the fields appear as columns. TFDMemTable can even be setup as persistent so that it automatically loads and saves the data contained within it (see demo) just like as if you had used SQLite.
TFDMemTable LocalSQL And Performance
I’ve easily used over 100,000 records in a TFDMemTable on the Windows platform and it was still pretty fast. TFDMemTable can be defined as a virtual table using the TFDLocalSQL component (see demo) and then queried as if it was an SQLite table using TFDQuery and TFDTable. There are a number of performance tuning tricks for TFDMemTable available in the Embarcadero DocWiki. These tricks include turning off transaction logging, setting a record maximum, and wrapping changes in a BeginBatch EndBatch clause. When I utilize TFDMemTable with a large number of records I usually deploy the code provided in the DocWiki and see significant performance improvements.
There are a number of different ways to search a TFDMemTable. Some of them require an index and some of them do not. The two main ways I’ve used are TFDMemTable.Locate() and TFDMemTable.Lookup(). The Locate() function moves the cursor of the dataset to the record if it is found while the Lookup() function does not move the cursor to the found record and instead returns the found data. Locate() and Lookup() are not unique to TFDMemTable and can be used with most FireDAC based datasets. There is a third way to search a TFDMemTable and that is through the TFDMemTable.FindKey() method. FindKey() requires an index and I’ve heard it is faster than Locate() but I haven’t used it quite as much as Locate().
As we’ve seen in the above post it is really easy to work with text based files and create structured data files like JSON, XML, and CSV in Delphi FireMonkey using both TStringList and TFDMemTable. These two objects work cross platform on Android, IOS, Macos, Windows, and Linux and can really increase developer productivity through their use. Do you use TStringList and TFDMemTable? If not what do you use instead and why?