📄 jclhashmaps.pas
字号:
It: IJclIntfIterator;
Key: IInterface;
{$IFDEF THREADSAFE}
CS: IInterface;
{$ENDIF THREADSAFE}
begin
{$IFDEF THREADSAFE}
CS := EnterCriticalSection;
{$ENDIF THREADSAFE}
if AMap = nil then
Exit;
It := AMap.KeySet.First;
while It.HasNext do
begin
Key := It.Next;
PutValue(Key, AMap.GetValue(Key));
end;
end;
procedure TJclIntfIntfHashMap.PutValue(Key, Value: IInterface);
var
Index: Integer;
Bucket: PJclIntfIntfBucket;
I: Integer;
{$IFDEF THREADSAFE}
CS: IInterface;
{$ENDIF THREADSAFE}
begin
{$IFDEF THREADSAFE}
CS := EnterCriticalSection;
{$ENDIF THREADSAFE}
if Key = nil then
Exit;
if Value = nil then
Exit;
Index := FHashFunction(Integer(Key));
Bucket := @FBuckets[Index];
for I := 0 to Bucket.Count - 1 do
if Bucket.Entries[I].Key = Key then
begin
Bucket.Entries[I].Value := Value;
Exit;
end;
if Bucket.Count = Length(Bucket.Entries) then
GrowEntries(Index);
Bucket.Entries[Bucket.Count].Key := Key;
Bucket.Entries[Bucket.Count].Value := Value;
Inc(Bucket.Count);
Inc(FCount);
end;
function TJclIntfIntfHashMap.Remove(Key: IInterface): IInterface;
var
Bucket: PJclIntfIntfBucket;
I: Integer;
{$IFDEF THREADSAFE}
CS: IInterface;
{$ENDIF THREADSAFE}
begin
{$IFDEF THREADSAFE}
CS := EnterCriticalSection;
{$ENDIF THREADSAFE}
Result := nil;
if Key = nil then
Exit;
Bucket := @FBuckets[FHashFunction(Integer(Key))];
for I := 0 to Bucket.Count - 1 do
if Bucket.Entries[I].Key = Key then
begin
Result := Bucket.Entries[I].Value;
if I < Length(Bucket.Entries) - 1 then
System.Move(Bucket.Entries[I + 1], Bucket.Entries[I],
(Bucket.Count - I) * SizeOf(TJclStrStrEntry));
Dec(Bucket.Count);
Break;
end;
end;
function TJclIntfIntfHashMap.Size: Integer;
begin
Result := FCount;
end;
function TJclIntfIntfHashMap.Values: IJclIntfCollection;
var
I, J: Integer;
{$IFDEF THREADSAFE}
CS: IInterface;
{$ENDIF THREADSAFE}
begin
{$IFDEF THREADSAFE}
CS := EnterCriticalSection;
{$ENDIF THREADSAFE}
Result := TJclIntfArrayList.Create(FCapacity);
for I := 0 to FCapacity - 1 do
for J := 0 to FBuckets[I].Count - 1 do
Result.Add(FBuckets[I].Entries[J].Value);
end;
//=== { TJclStrIntfHashMap } =================================================
constructor TJclStrIntfHashMap.Create(ACapacity: Integer = DefaultContainerCapacity);
var
I: Integer;
begin
inherited Create;
if ACapacity < 0 then
FCapacity := 0
else
FCapacity := ACapacity;
SetLength(FBuckets, FCapacity);
for I := 0 to FCapacity - 1 do
SetLength(FBuckets[I].Entries, 1);
FHashFunction := HashMul;
end;
destructor TJclStrIntfHashMap.Destroy;
begin
Clear;
inherited Destroy;
end;
procedure TJclStrIntfHashMap.Clear;
var
I, J: Integer;
{$IFDEF THREADSAFE}
CS: IInterface;
{$ENDIF THREADSAFE}
begin
{$IFDEF THREADSAFE}
CS := EnterCriticalSection;
{$ENDIF THREADSAFE}
for I := 0 to FCapacity - 1 do
begin
for J := 0 to FBuckets[I].Count - 1 do
begin
FBuckets[I].Entries[J].Key := '';
FBuckets[I].Entries[J].Value := nil;
end;
FBuckets[I].Count := 0;
end;
FCount := 0;
end;
function TJclStrIntfHashMap.Clone: IInterface;
var
I, J: Integer;
NewEntryArray: TJclStrIntfEntryArray;
NewMap: TJclStrIntfHashMap;
{$IFDEF THREADSAFE}
CS: IInterface;
{$ENDIF THREADSAFE}
begin
{$IFDEF THREADSAFE}
CS := EnterCriticalSection;
{$ENDIF THREADSAFE}
NewMap := TJclStrIntfHashMap.Create(FCapacity);
for I := 0 to FCapacity - 1 do
begin
NewEntryArray := NewMap.FBuckets[I].Entries;
SetLength(NewEntryArray, Length(FBuckets[I].Entries));
for J := 0 to FBuckets[I].Count - 1 do
begin
NewEntryArray[J].Key := FBuckets[I].Entries[J].Key;
NewEntryArray[J].Value := FBuckets[I].Entries[J].Value;
end;
NewMap.FBuckets[I].Count := FBuckets[I].Count;
end;
Result := NewMap;
end;
function TJclStrIntfHashMap.ContainsKey(const Key: string): Boolean;
var
I: Integer;
Bucket: PJclStrIntfBucket;
{$IFDEF THREADSAFE}
CS: IInterface;
{$ENDIF THREADSAFE}
begin
{$IFDEF THREADSAFE}
CS := EnterCriticalSection;
{$ENDIF THREADSAFE}
Result := False;
if Key = '' then
Exit;
Bucket := @FBuckets[FHashFunction(HashString(Key))];
for I := 0 to Bucket.Count - 1 do
if Bucket.Entries[I].Key = Key then
begin
Result := True;
Break;
end;
end;
function TJclStrIntfHashMap.ContainsValue(Value: IInterface): Boolean;
var
I, J: Integer;
Bucket: PJclStrIntfBucket;
{$IFDEF THREADSAFE}
CS: IInterface;
{$ENDIF THREADSAFE}
begin
{$IFDEF THREADSAFE}
CS := EnterCriticalSection;
{$ENDIF THREADSAFE}
Result := False;
if Value = nil then
Exit;
for J := 0 to FCapacity - 1 do
begin
Bucket := @FBuckets[J];
for I := 0 to Bucket.Count - 1 do
if Bucket.Entries[I].Value = Value then
begin
Result := True;
Exit;
end;
end;
end;
function TJclStrIntfHashMap.Equals(AMap: IJclStrIntfMap): Boolean;
var
I, J: Integer;
{$IFDEF THREADSAFE}
CS: IInterface;
{$ENDIF THREADSAFE}
begin
{$IFDEF THREADSAFE}
CS := EnterCriticalSection;
{$ENDIF THREADSAFE}
Result := False;
if AMap = nil then
Exit;
if FCount <> AMap.Size then
Exit;
Result := True;
for I := 0 to FCapacity - 1 do
for J := 0 to FBuckets[I].Count - 1 do
if AMap.ContainsKey(FBuckets[I].Entries[J].Key) then
begin
if AMap.GetValue(FBuckets[I].Entries[J].Key) <>
FBuckets[I].Entries[J].Value then
begin
Result := False;
Exit;
end;
end
else
begin
Result := False;
Exit;
end;
end;
function TJclStrIntfHashMap.GetValue(const Key: string): IInterface;
var
I: Integer;
Index: Integer;
Bucket: PJclStrIntfBucket;
{$IFDEF THREADSAFE}
CS: IInterface;
{$ENDIF THREADSAFE}
begin
{$IFDEF THREADSAFE}
CS := EnterCriticalSection;
{$ENDIF THREADSAFE}
Result := nil;
if Key = '' then
Exit;
Index := FHashFunction(HashString(Key));
Bucket := @FBuckets[Index];
for I := 0 to Bucket.Count - 1 do
if Bucket.Entries[I].Key = Key then
begin
Result := Bucket.Entries[I].Value;
Break;
end;
end;
procedure TJclStrIntfHashMap.GrowEntries(BucketIndex: Integer);
var
Capacity: Integer;
begin
Capacity := Length(FBuckets[BucketIndex].Entries);
if Capacity > 64 then
Capacity := Capacity + Capacity div 4
else
Capacity := Capacity * 4;
SetLength(FBuckets[BucketIndex].Entries, Capacity);
end;
function TJclStrIntfHashMap.HashMul(Key: Cardinal): Cardinal;
const
A = 0.6180339887; // (sqrt(5) - 1) / 2
begin
Result := Trunc(FCapacity * (Frac(Key * A)));
end;
function TJclStrIntfHashMap.HashString(const Key: string): Cardinal;
var
I: Integer;
{$IFDEF THREADSAFE}
CS: IInterface;
{$ENDIF THREADSAFE}
begin
{$IFDEF THREADSAFE}
CS := EnterCriticalSection;
{$ENDIF THREADSAFE}
Result := 0;
for I := 1 to Length(Key) do
Result := Result + Cardinal(Ord(Key[I]) * (I - 1) * 256);
end;
function TJclStrIntfHashMap.IsEmpty: Boolean;
begin
Result := FCount = 0;
end;
function TJclStrIntfHashMap.KeySet: IJclStrSet;
var
I, J: Integer;
{$IFDEF THREADSAFE}
CS: IInterface;
{$ENDIF THREADSAFE}
begin
{$IFDEF THREADSAFE}
CS := EnterCriticalSection;
{$ENDIF THREADSAFE}
Result := TJclStrArraySet.Create(FCapacity);
for I := 0 to FCapacity - 1 do
for J := 0 to FBuckets[I].Count - 1 do
Result.Add(FBuckets[I].Entries[J].Key);
end;
procedure TJclStrIntfHashMap.PutAll(AMap: IJclStrIntfMap);
var
It: IJclStrIterator;
Key: string;
{$IFDEF THREADSAFE}
CS: IInterface;
{$ENDIF THREADSAFE}
begin
{$IFDEF THREADSAFE}
CS := EnterCriticalSection;
{$ENDIF THREADSAFE}
if AMap = nil then
Exit;
It := AMap.KeySet.First;
while It.HasNext do
begin
Key := It.Next;
PutValue(Key, AMap.GetValue(Key));
end;
end;
procedure TJclStrIntfHashMap.PutValue(const Key: string; Value: IInterface);
var
Index: Integer;
Bucket: PJclStrIntfBucket;
I: Integer;
{$IFDEF THREADSAFE}
CS: IInterface;
{$ENDIF THREADSAFE}
begin
{$IFDEF THREADSAFE}
CS := EnterCriticalSection;
{$ENDIF THREADSAFE}
if Key = '' then
Exit;
if Value = nil then
Exit;
Index := FHashFunction(HashString(Key));
Bucket := @FBuckets[Index];
for I := 0 to Bucket.Count - 1 do
if Bucket.Entries[I].Key = Key then
begin
Bucket.Entries[I].Value := Value;
Exit;
end;
if Bucket.Count = Length(Bucket.Entries) then
GrowEntries(Index);
Bucket.Entries[Bucket.Count].Key := Key;
Bucket.Entries[Bucket.Count].Value := Value;
Inc(Bucket.Count);
Inc(FCount);
end;
function TJclStrIntfHashMap.Remove(const Key: string): IInterface;
var
Bucket: PJclStrIntfBucket;
I: Integer;
{$IFDEF THREADSAFE}
CS: IInterface;
{$ENDIF THREADSAFE}
begin
{$IFDEF THREADSAFE}
CS := EnterCriticalSection;
{$ENDIF THREADSAFE}
Result := nil;
if Key = '' then
Exit;
Bucket := @FBuckets[FHashFunction(HashString(Key))];
for I := 0 to Bucket.Count - 1 do
if Bucket.Entries[I].Key = Key then
begin
Result := Bucket.Entries[I].Value;
if I < Length(Bucket.Entries) - 1 then
System.Move(Bucket.Entries[I + 1], Bucket.Entries[I],
(Bucket.Count - I) * SizeOf(TJclStrStrEntry));
Dec(Bucket.Count);
Break;
end;
end;
function TJclStrIntfHashMap.Size: Integer;
begin
Result := FCount;
end;
function TJclStrIntfHashMap.Values: IJclIntfCollection;
var
I, J: Integer;
{$IFDEF THREADSAFE}
CS: IInterface;
{$ENDIF THREADSAFE}
begin
{$IFDEF THREADSAFE}
CS := EnterCriticalSection;
{$ENDIF THREADSAFE}
Result := TJclIntfArrayList.Create;
for I := 0 to FCapacity - 1 do
for J := 0 to FBuckets[I].Count - 1 do
Result.Add(FBuckets[I].Entries[J].Value);
end;
//=== { TJclStrStrHashMap } ==================================================
constructor TJclStrStrHashMap.Create(ACapacity: Integer = DefaultContainerCapacity);
var
I: Integer;
begin
inherited Create;
if ACapacity < 0 then
FCapacity := 0
else
FCapacity := ACapacity;
SetLength(FBuckets, FCapacity);
for I := 0 to FCapacity - 1 do
SetLength(FBuckets[I].Entries, 1);
FHashFunction := HashMul;
end;
destructor TJclStrStrHashMap.Destroy;
begin
Clear;
inherited Destroy;
end;
procedure TJclStrStrHashMap.Clear;
var
I, J: Integer;
{$IFDEF THREADSAFE}
CS: IInterface;
{$ENDIF THREADSAFE}
begin
{$IFDEF THREADSAFE}
CS := EnterCriticalSection;
{$ENDIF THREADSAFE}
for I := 0 to FCapacity - 1 do
begin
for J := 0 to FBuckets[I].Count - 1 do
begin
FBuckets[I].Entries[J].Key := '';
FBuckets[I].Entries[J].Value := '';
end;
FBuckets[I].Count := 0;
end;
FCount := 0;
end;
function TJclStrStrHashMap.Clone: IInterface;
var
I, J: Integer;
NewEntryArray: TJclStrStrEntryArray;
NewMap: TJclStrStrHashMap;
{$IFDEF THREADSAFE}
CS: IInterface;
{$ENDIF THREADSAFE}
begin
{$IFDEF THREADSAFE}
CS := EnterCriticalSection;
{$ENDIF THREADSAFE}
NewMap := TJclStrStrHashMap.Create(FCapacity);
for I := 0 to FCapacity - 1 do
begin
NewEntryArray := NewMap.FBuckets[I].Entries;
SetLength(NewEntryArray, Length(FBuckets[I].Entries));
for J := 0 to FBuckets[I].Count - 1 do
begin
NewEntryArray[J].Key := FBuckets[I].Entries[J].Key;
NewEntryArray[J].Value := FBuckets[I].Entries[J].Value;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -