Efficient data structure for GUIDs

后端 未结 3 1264
南笙
南笙 2020-12-30 07:01

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

3条回答
  •  难免孤独
    2020-12-30 07:36

    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;
    

提交回复
热议问题