mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-12-16 02:00:30 +01:00
LazUtils: change DictionaryStringList to support duplicates. It does not store indexes in the map any more.
git-svn-id: trunk@40537 -
This commit is contained in:
parent
56cdd910fe
commit
aeba11b9c8
@ -16,18 +16,15 @@
|
||||
|
||||
Abstract:
|
||||
This is an unsorted StringList with a fast lookup feature.
|
||||
Internally it uses a string->integer map container to store
|
||||
the string again as key and string's index as value.
|
||||
Internally it uses a string map container to store the string again.
|
||||
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.
|
||||
All Duplicates property values are fully supported,
|
||||
including dupIgnore and dupError, 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, or must prevent duplicates.
|
||||
}
|
||||
@ -46,29 +43,20 @@ 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);
|
||||
FMap: TStringMap;
|
||||
// FAssigning: Boolean;
|
||||
protected
|
||||
procedure InsertItem(Index: Integer; const S: string); override;
|
||||
procedure InsertItem(Index: Integer; const S: string; O: TObject); override;
|
||||
public
|
||||
constructor Create;
|
||||
destructor Destroy; override;
|
||||
procedure Assign(Source: TPersistent); override;
|
||||
procedure Clear; override;
|
||||
procedure Delete(Index: Integer); override;
|
||||
procedure Exchange(Index1, Index2: Integer); override;
|
||||
function Contains(const S: string): Boolean; // A new function
|
||||
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;
|
||||
|
||||
|
||||
@ -79,7 +67,7 @@ implementation
|
||||
constructor TDictionaryStringList.Create;
|
||||
begin
|
||||
inherited Create;
|
||||
FMap := TStringToPointerTree.Create(True);
|
||||
FMap := TStringMap.Create(True);
|
||||
end;
|
||||
|
||||
destructor TDictionaryStringList.Destroy;
|
||||
@ -88,17 +76,16 @@ begin
|
||||
inherited Destroy;
|
||||
end;
|
||||
|
||||
procedure TDictionaryStringList.AdjustMap(StartIndex, Offset: Integer);
|
||||
// Adjust all indexes >= startindex in map.
|
||||
// This is needed after inserting or deleting an item.
|
||||
var
|
||||
i: Integer;
|
||||
Ind: PtrInt;
|
||||
procedure TDictionaryStringList.Assign(Source: TPersistent);
|
||||
begin
|
||||
for i := StartIndex to Count-1 do
|
||||
if Source is TStrings then
|
||||
begin
|
||||
Ind := PtrInt(FMap[Strings[i]]);
|
||||
FMap[Strings[i]] := Pointer(Ind + Offset);
|
||||
// FAssigning := True;
|
||||
// This crashes when the Source is a normal TStringList. Why?
|
||||
inherited Assign(Source);
|
||||
if Source is TDictionaryStringList then
|
||||
FMap.Assign(TDictionaryStringList(Source).FMap);
|
||||
// FAssigning := False;
|
||||
end;
|
||||
end;
|
||||
|
||||
@ -109,29 +96,21 @@ begin
|
||||
end;
|
||||
|
||||
procedure TDictionaryStringList.Delete(Index: Integer);
|
||||
begin
|
||||
inherited Delete(Index);
|
||||
// Decrement Indexes in map above the deleted item.
|
||||
AdjustMap(Index, -1);
|
||||
end;
|
||||
|
||||
procedure TDictionaryStringList.Exchange(Index1, Index2: Integer);
|
||||
var
|
||||
s1, s2: string;
|
||||
s: String;
|
||||
begin
|
||||
S1 := Strings[Index1];
|
||||
S2 := Strings[Index2];
|
||||
inherited Exchange(Index1, Index2);
|
||||
// Exchange Indexes in map, too.
|
||||
FMap[s1] := Pointer(PtrInt(Index2));
|
||||
FMap[s2] := Pointer(PtrInt(Index1));
|
||||
s := Strings[Index];
|
||||
inherited Delete(Index);
|
||||
// The string must not be deleted from map if there are duplicates.
|
||||
// Calling IndexOf is slow but it is needed.
|
||||
if (Duplicates <> dupAccept) or (inherited IndexOf(s) = -1) then
|
||||
FMap.Remove(s);
|
||||
end;
|
||||
|
||||
procedure TDictionaryStringList.InsertItem(Index: Integer; const S: string);
|
||||
var
|
||||
i: PtrInt;
|
||||
begin
|
||||
if Duplicates <> dupAccept then
|
||||
// if FAssigning then Exit;
|
||||
if (Duplicates <> dupAccept) and not Sorted then
|
||||
if IndexOf(S) <> -1 then
|
||||
case Duplicates of
|
||||
DupIgnore : Exit;
|
||||
@ -139,10 +118,7 @@ begin
|
||||
+' 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);
|
||||
FMap.Add(S); // Insert string to map, too.
|
||||
end;
|
||||
|
||||
procedure TDictionaryStringList.InsertItem(Index: Integer; const S: string; O: TObject);
|
||||
@ -165,54 +141,10 @@ end;
|
||||
function TDictionaryStringList.IndexOf(const S: string): Integer;
|
||||
begin
|
||||
if FMap.Contains(S) then
|
||||
Result := integer(PtrInt(FMap[S])) // Index is stored in the map.
|
||||
Result := inherited IndexOf(S)
|
||||
else
|
||||
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.GetDuplicates: TDuplicates;
|
||||
begin
|
||||
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.
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user