LazUtils: improve TDictionaryStringList

git-svn-id: trunk@40525 -
This commit is contained in:
juha 2013-03-10 10:33:49 +00:00
parent a0e36fb682
commit d3515b51b0

View File

@ -16,26 +16,20 @@
Abstract:
This is an unsorted StringList with a fast lookup feature.
Internally it uses a map container (TStringToPointerTree) to store the string again
as key and string's index as value.
It is then used for Contains, IndexOf and Find methods.
Internally it uses a string->integer map container to store
the string again as key and string's index as value.
It is then used for InserItem, Contains, IndexOf and Find methods.
The extra container does not reserve too much memory because the strings are
reference counted and not really copied.
The list cannot have duplicates.
Duplicates property values dupIgnore and dupError are fully supported,
unlike in unsorted StringList.
This list cannot be sorted. For a sorted list you should use normal TStringList.
This class is useful only when you must preserve the order in list,
but also need to do fast lookups to see if a string exists.
"Duplicates" values dupIgnore and dupError are supported, unlike in unsorted StringList.
Insert, Delete and Exchange are not supported yet. They require the map's
index values to be adjusted. There are at least 3 ways to solve it:
1. Adjust only the changed indexes after each operation.
2. Mark the map's index values as "dirty" after those operations.
Adjust all the indexes when IndexOf() or Find() is called for the first time.
3. Decide that those operations are not needed.
This is a very specialized container after all.
This class is useful only when you must preserve the order in list, but
also need to do fast lookups to see if a string exists, or must prevent duplicates.
}
unit DictionaryStringList;
@ -53,16 +47,17 @@ type
TDictionaryStringList = class(TStringList)
private
FMap: TStringToPointerTree;
function GetDuplicates: TDuplicates;
function GetSorted: Boolean;
procedure SetDuplicates(AValue: TDuplicates);
procedure SetSorted(AValue: Boolean);
procedure AdjustMap(StartIndex, Offset: Integer);
protected
// procedure InsertItem(Index: Integer; const S: string); override;
// procedure InsertItem(Index: Integer; const S: string; O: TObject); override;
procedure InsertItem(Index: Integer; const S: string); override;
procedure InsertItem(Index: Integer; const S: string; O: TObject); override;
public
constructor Create;
destructor Destroy; override;
function Add(const S: string): Integer; override;
procedure Insert(Index: Integer; const S: string); override;
procedure Clear; override;
procedure Delete(Index: Integer); override;
procedure Exchange(Index1, Index2: Integer); override;
@ -70,7 +65,9 @@ type
function Find(const S: string; out Index: Integer): Boolean; override;
function IndexOf(const S: string): Integer; override;
procedure Sort; override;
procedure ValidateMap; // For debugging purposes only
public
property Duplicates: TDuplicates read GetDuplicates write SetDuplicates;
property Sorted: Boolean read GetSorted write SetSorted;
end;
@ -91,39 +88,20 @@ begin
inherited Destroy;
end;
function TDictionaryStringList.Add(const S: string): Integer;
procedure TDictionaryStringList.AdjustMap(StartIndex, Offset: Integer);
// Adjust all indexes >= startindex in map.
// This is needed after inserting or deleting an item.
var
i: PtrInt;
i: Integer;
Ind: PtrInt;
begin
Result := -1;
if Duplicates <> dupAccept then
if IndexOf(S) <> -1 then
case Duplicates of
DupIgnore : Exit;
DupError : raise Exception.Create('TDictionaryStringList.Add: Duplicates are not allowed.');
end;
Result := inherited Add(S);
i := Result;
FMap[S] := Pointer(i); // Store index to map.
for i := StartIndex to Count-1 do
begin
Ind := PtrInt(FMap[Strings[i]]);
FMap[Strings[i]] := Pointer(Ind + Offset);
end;
end;
procedure TDictionaryStringList.Insert(Index: Integer; const S: string);
begin
raise Exception.Create('TDictionaryStringList.Insert is not implemented yet.');
inherited Insert(Index, S);
// ToDo: adjust all indexes in FMap after the item is inserted.
end;
{
procedure TDictionaryStringList.InsertItem(Index: Integer; const S: string);
begin
inherited InsertItem(Index, S);
end;
procedure TDictionaryStringList.InsertItem(Index: Integer; const S: string; O: TObject);
begin
inherited InsertItem(Index, S, O);
end;
}
procedure TDictionaryStringList.Clear;
begin
inherited Clear;
@ -132,16 +110,45 @@ end;
procedure TDictionaryStringList.Delete(Index: Integer);
begin
raise Exception.Create('TDictionaryStringList.Delete is not implemented yet.');
inherited Delete(Index);
// ToDo: adjust all indexes in FMap after the item is deleted.
// Decrement Indexes in map above the deleted item.
AdjustMap(Index, -1);
end;
procedure TDictionaryStringList.Exchange(Index1, Index2: Integer);
var
s1, s2: string;
begin
raise Exception.Create('TDictionaryStringList.Exchange is not implemented yet.');
S1 := Strings[Index1];
S2 := Strings[Index2];
inherited Exchange(Index1, Index2);
// ToDo: adjust all indexes in FMap after Exchange.
// Exchange Indexes in map, too.
FMap[s1] := Pointer(PtrInt(Index2));
FMap[s2] := Pointer(PtrInt(Index1));
end;
procedure TDictionaryStringList.InsertItem(Index: Integer; const S: string);
var
i: PtrInt;
begin
if Duplicates <> dupAccept then
if IndexOf(S) <> -1 then
case Duplicates of
DupIgnore : Exit;
DupError : raise Exception.Create('TDictionaryStringList.InsertItem:'
+' Duplicates are not allowed.');
end;
inherited InsertItem(Index, S);
i := Index;
FMap[S] := Pointer(i); // Store index to map.
// Increment Indexes in map above the inserted item.
AdjustMap(Index+1, 1);
end;
procedure TDictionaryStringList.InsertItem(Index: Integer; const S: string; O: TObject);
begin
raise Exception.Create('TDictionaryStringList.InsertItem: is this needed?');
//inherited InsertItem(Index, S, O);
end;
function TDictionaryStringList.Contains(const S: string): Boolean;
@ -163,20 +170,48 @@ begin
Result := -1
end;
procedure TDictionaryStringList.ValidateMap;
// For debugging purposes only
var
i, Ind: Integer;
s: String;
begin
for i := 0 to Count-1 do
begin
s := Strings[i];
Ind := integer(PtrInt(FMap[s]));
if Ind <> i then
raise Exception.CreateFmt('Indexes for string "%s" differ, list:%d, map:%d.',
[s, i, Ind]);
end;
end;
procedure TDictionaryStringList.Sort;
begin
raise Exception.Create('This list is not meant to be sorted. Use TStringList instead.');
end;
function TDictionaryStringList.GetSorted: Boolean;
begin
Result := False;
end;
procedure TDictionaryStringList.SetSorted(AValue: Boolean);
begin
if AValue then
Sort; // Raise an exception here, too.
end;
function TDictionaryStringList.GetSorted: Boolean;
function TDictionaryStringList.GetDuplicates: TDuplicates;
begin
Result := False;
Result := inherited Duplicates;
end;
procedure TDictionaryStringList.SetDuplicates(AValue: TDuplicates);
begin
if AValue = dupAccept then
raise Exception.Create('Sorry, TDictionaryStringList does not support duplicates.');
inherited Duplicates := AValue;
end;
end.