I am in search of a data structure which enables me to quickly (prefarably O(1)-quickly) determine if a given GUID is a member of a Collection of GUIDs or not.
My cu
type
PGuidDictionaryItem = ^TGuidDictionaryItem;
TGuidDictionaryItem = record
Key: TGuid;
Value: Pointer;
Next: PGuidDictionaryItem;
end;
TGuidDictionary = class
private
const
HashSize = 2048;
var
Size: integer;
FTable: array [0..HashSize-1] of PGuidDictionaryItem;
function GetHashCode(Guid: TGUID): integer;
public
constructor Create;
destructor Destroy; override;
procedure Add(Key: TGUID; Value: TObject);
function TryFind(Key: TGUID; out Value: TObject): boolean;
function Contains(Key: TGUID): Boolean;
procedure Remove(Key: TGuid);
end;
{ TGuidDictionary }
procedure TGuidDictionary.Add(Key: TGUID; Value: TObject);
var
Hc: integer;
PHi: PGuidDictionaryItem;
begin
Hc := GetHashCode(Key);
if FTable[Hc] <> nil then
begin
PHi := FTable[Hc];
repeat
if TGuidEx.EqualGuids(PHi.Key, Key) then
Break;
PHi := Phi.Next;
until PHi = nil;
end
else
Phi := nil;
if PHi <> nil then
PHi.Value := Value
else
begin
New(PHi);
PHi.Value := Value;
PHi.Key := Key;
PHi.Next := FTable[Hc];
FTable[Hc] := PHi;
end;
end;
function TGuidDictionary.Contains(Key: TGUID): Boolean;
var
O: TObject;
begin
Result := TryFind(Key, O);
end;
constructor TGuidDictionary.Create;
var
i: integer;
begin
inherited;
for i := Low(FTable) to High(FTable) do
FTable[i] := nil;
end;
destructor TGuidDictionary.Destroy;
var
i: integer;
Phi, PhiNext: PGuidDictionaryItem;
begin
for i := Low(FTable) to High(FTable) do
begin
Phi := FTable[i];
while Phi <> nil do
begin
PhiNext := Phi.Next;
Dispose(Phi);
Phi := PhiNext;
end;
end;
inherited;
end;
function TGuidDictionary.GetHashCode(Guid: TGUID): integer;
var
N: array [0..3] of integer absolute Guid;
begin
Result := Abs(N[0] xor N[1] xor N[2] xor N[3]) mod HashSize;
end;
procedure TGuidDictionary.Remove(Key: TGuid);
var
Hc: Integer;
Phi, BeforPhi: PGuidDictionaryItem;
begin
Hc := GetHashCode(Key);
BeforPhi := nil;
Phi := FTable[Hc];
while (Phi <> nil) and not TGuidEx.EqualGuids(Phi.Key, Key) do
begin
BeforPhi := Phi;
Phi := Phi.Next;
end;
if Phi = nil then
Exit;
if BeforPhi <> nil then
BeforPhi.Next := Phi.Next
else
FTable[Hc] := Phi.Next;
Dispose(Phi);
end;
function TGuidDictionary.TryFind(Key: TGUID; out Value: TObject): boolean;
var
Hc: Integer;
Phi: PGuidDictionaryItem;
begin
Hc := GetHashCode(Key);
Phi := FTable[Hc];
while (Phi <> nil) and not TGuidEx.EqualGuids(Phi.Key, Key) do
Phi := Phi.Next;
if Phi <> nil then
Value := TObject(Phi.Value)
else
Value := nil;
Result := Phi <> nil;
end;
procedure TestDictMisc.TestGuidDictionary;
const
G1: TGUID = '{68D09F12-3E0D-4963-B32C-4EE3BD90F69C}';
G2: TGUID = '{BEED37F6-9757-41DC-8463-AF094392652B}';
var
T: TGuidDictionary;
Obj1, Obj2, O: TObject;
begin
T := TGuidDictionary.Create;
Obj1 := TObject.Create();
Obj2 := TObject.Create();
try
CheckFalse(T.Contains(G1));
T.Add(G1, Obj1);
CheckTrue(T.Contains(G1));
T.Add(G2, Obj2);
CheckTrue(T.Contains(G2));
T.Add(G2, Obj2);
CheckTrue(T.Contains(G2));
CheckTrue(T.TryFind(G1, {out} O));
CheckSame(Obj1, O);
CheckTrue(T.TryFind(G2, {out} O));
CheckSame(Obj2, O);
T.Remove(G1);
CheckFalse(T.Contains(G1));
CheckFalse(T.TryFind(G1, {out} O));
T.Add(G1, Obj1);
CheckTrue(T.TryFind(G1, {out} O));
CheckSame(Obj1, O);
finally
Obj1.Free();
Obj2.Free();
T.Free;
end;
end;