I did a quick benchmark of making ordered and unordered insertion in a TFPGMap, both sorted and unsorted, and while ordered insertion is reasonably slow (IIRC a sorted list is the worst scenario for quicksort), both ordered and unordered insertions are so much faster when sorted.
@ Leledumbo: That TwoWayMap is such a great approach of what I'd need. I don't think I could write a custom two way map where both keys and data are hashed or sorted in some way, but that usage of nested maps and overriding the needed methods is genius. I think I can live with the extra memory requirement, because once the implementation is done, using it will be so easy.
I didn't want to put any code because the way I use it is not exactly <string, integer>, and the code is actually spread in several functions. What Leledumbo said is enough for anyone to start using a TFPGMap, so putting my code as an example is not needed IMHO (and it's a PITA to translate all the identifiers). However I think I could write down a quick example and post it, so if anyone searching for "how to use fgl TFPGMap" hits this thread can have real code to read.
And I don't think I can optimize it anymore without making it unreadable/uncomprehensible. I got it down to 3 seconds and I think I got the TFPGMap thing right and straight. There is another place where I can change a TStringListEx with this TFPGMap thingy, so when my code advances to make heavy use of that part, it'll be faster than it could have been without the map.
If anyone is curious, it's a custom file renamer to maintain a large collection of files, using certain words in the filename to categorize and rename accordingly in a certain way. For now it's 26,000 files, so making this as a pet/personal project was not a bad idea. Last time I counted there were like 13,000 of those keywords , and probably have some thousands more to find and categorize. The renamer also detects uncategorized keywords to help in maintaining the collection with the best possible names using that information.
Thanks everyone for all the help.
edit: Here you have some test code using the most common features of TFPGMap.
program testmap;
uses
fgl;
type
TDictionary = specialize TFPGMap<string, integer>;
var
i: integer;
dict: TDictionary;
begin
dict := TDictionary.Create;
dict.Sorted := True; // for fast lookup of keys
// assign initial values
dict['one'] := 1;
dict['two'] := 2;
dict['three'] := 3;
// get data back
writeln('The value of ''one'' is ', dict['one']);
// if it's not known if the key is in there
// use Find for sorted maps, but use IndexOf otherwise
if dict.Find('four', i) then
begin
writeln('The value of ''four'' is ', dict['four']);
writeln('The value of ''four'' is ', dict.Data[i]);
end
else
begin
writeln('Can''t find key four, adding it');
dict['four'] := 4;
end;
// change one of the numbers
dict['one'] := 5;
writeln('The value of ''one'' is now ', dict['one']);
// iterate with the keys and/or data
writeln('All the keys and their values from the current map');
for i := 0 to dict.Count - 1 do
writeln(dict.Keys[i], '=', dict.Data[i]);
// delete one key (with its value) from the map
writeln('Removing ''two''');
dict.Remove('two');
writeln('All the keys and their values from the current map');
for i := 0 to dict.Count - 1 do
writeln(dict.Keys[i], '=', dict.Data[i]);
// find which key has a certain data (always sequential search, slow)
i := dict.IndexOfData(3);
writeln('Value 3 was found to have the keyname ', dict.Keys[i]);
dict.Free;
end.