📄 jclhashmaps.pas
字号:
end;
NewMap.FBuckets[I].Count := FBuckets[I].Count;
end;
Result := NewMap;
end;
function TJclStrStrHashMap.ContainsKey(const Key: string): Boolean;
var
I: Integer;
Bucket: PJclStrStrBucket;
{$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 TJclStrStrHashMap.ContainsValue(const Value: string): Boolean;
var
I, J: Integer;
Bucket: PJclStrStrBucket;
{$IFDEF THREADSAFE}
CS: IInterface;
{$ENDIF THREADSAFE}
begin
{$IFDEF THREADSAFE}
CS := EnterCriticalSection;
{$ENDIF THREADSAFE}
Result := False;
if Value = '' 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 TJclStrStrHashMap.Equals(AMap: IJclStrStrMap): 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 TJclStrStrHashMap.GetValue(const Key: string): string;
var
I: Integer;
Bucket: PJclStrStrBucket;
{$IFDEF THREADSAFE}
CS: IInterface;
{$ENDIF THREADSAFE}
begin
{$IFDEF THREADSAFE}
CS := EnterCriticalSection;
{$ENDIF THREADSAFE}
Result := '';
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;
Break;
end;
end;
procedure TJclStrStrHashMap.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 TJclStrStrHashMap.HashMul(Key: Cardinal): Cardinal;
const
A = 0.6180339887; // (sqrt(5) - 1) / 2
begin
Result := Trunc(FCapacity * (Frac(Key * A)));
end;
function TJclStrStrHashMap.IsEmpty: Boolean;
begin
Result := FCount = 0;
end;
function TJclStrStrHashMap.KeyOfValue(const Value: string): string;
var
I, J: Integer;
Bucket: PJclStrStrBucket;
{$IFDEF THREADSAFE}
CS: IInterface;
{$ENDIF THREADSAFE}
begin
{$IFDEF THREADSAFE}
CS := EnterCriticalSection;
{$ENDIF THREADSAFE}
if Value = '' 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 := Bucket.Entries[I].Key;
Exit;
end;
end;
raise EJclError.CreateResFmt(@RsEValueNotFound, [Value]);
end;
function TJclStrStrHashMap.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 TJclStrStrHashMap.PutAll(AMap: IJclStrStrMap);
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 TJclStrStrHashMap.PutValue(const Key, Value: string);
var
Index: Integer;
Bucket: PJclStrStrBucket;
I: Integer;
{$IFDEF THREADSAFE}
CS: IInterface;
{$ENDIF THREADSAFE}
begin
{$IFDEF THREADSAFE}
CS := EnterCriticalSection;
{$ENDIF THREADSAFE}
if Key = '' then
Exit;
if Value = '' 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 TJclStrStrHashMap.Remove(const Key: string): string;
var
Bucket: PJclStrStrBucket;
I: Integer;
{$IFDEF THREADSAFE}
CS: IInterface;
{$ENDIF THREADSAFE}
begin
{$IFDEF THREADSAFE}
CS := EnterCriticalSection;
{$ENDIF THREADSAFE}
Result := '';
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 TJclStrStrHashMap.Size: Integer;
begin
Result := FCount;
end;
function TJclStrStrHashMap.Values: IJclStrCollection;
var
I, J: Integer;
{$IFDEF THREADSAFE}
CS: IInterface;
{$ENDIF THREADSAFE}
begin
{$IFDEF THREADSAFE}
CS := EnterCriticalSection;
{$ENDIF THREADSAFE}
Result := TJclStrArrayList.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;
function TJclStrStrHashMap.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;
//=== { TJclStrHashMap } =====================================================
constructor TJclStrHashMap.Create(ACapacity: Integer = DefaultContainerCapacity;
AOwnsObjects: Boolean = True);
var
I: Integer;
begin
inherited Create;
FOwnsObjects := AOwnsObjects;
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 TJclStrHashMap.Destroy;
begin
Clear;
inherited Destroy;
end;
procedure TJclStrHashMap.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 := '';
FreeObject(FBuckets[I].Entries[J].Value);
end;
FBuckets[I].Count := 0;
end;
FCount := 0;
end;
function TJclStrHashMap.Clone: TObject;
var
I, J: Integer;
NewEntryArray: TJclStrEntryArray;
NewMap: TJclStrHashMap;
{$IFDEF THREADSAFE}
CS: IInterface;
{$ENDIF THREADSAFE}
begin
{$IFDEF THREADSAFE}
CS := EnterCriticalSection;
{$ENDIF THREADSAFE}
NewMap := TJclStrHashMap.Create(FCapacity, False);
// Only one can have FOwnsObjects = True
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 TJclStrHashMap.ContainsKey(const Key: string): Boolean;
var
I: Integer;
Bucket: PJclStrBucket;
{$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 TJclStrHashMap.ContainsValue(Value: TObject): Boolean;
var
I, J: Integer;
Bucket: PJclStrBucket;
{$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 TJclStrHashMap.Equals(AMap: IJclStrMap): 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 TJclStrHashMap.GetValue(const Key: string): TObject;
var
I: Integer;
Bucket: PJclStrBucket;
{$IFDEF THREADSAFE}
CS: IInterface;
{$ENDIF THREADSAFE}
begin
{$IFDEF THREADSAFE}
CS := EnterCriticalSection;
{$ENDIF THREADSAFE}
Result := nil;
if Key = '' then
Exit;
I := FHashFunction(HashString(Key));
Bucket := @FBuckets[I];
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 TJclStrHashMap.FreeObject(var AObject: TObject);
begin
if FOwnsObjects then
begin
AObject.Free;
AObject := nil;
end;
end;
procedure TJclStrHashMap.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 TJclStrHashMap.HashMul(Key: Cardinal): Cardinal;
const
A = 0.6180339887; // (sqrt(5) - 1) / 2
begin
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -