📄 jclhashmaps.pas
字号:
Result := Trunc(FCapacity * (Frac(Key * A)));
//Result := LongRec(Key).Bytes[1] and $FF;
end;
function TJclStrHashMap.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 TJclStrHashMap.IsEmpty: Boolean;
begin
Result := FCount = 0;
end;
function TJclStrHashMap.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 TJclStrHashMap.PutAll(AMap: IJclStrMap);
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 TJclStrHashMap.PutValue(const Key: string; Value: TObject);
var
Index: Integer;
Bucket: PJclStrBucket;
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 TJclStrHashMap.Remove(const Key: string): TObject;
var
Bucket: PJclStrBucket;
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
if not FOwnsObjects then
Result := Bucket.Entries[I].Value
else
Bucket.Entries[I].Value.Free;
if I < Length(Bucket.Entries) - 1 then
System.Move(Bucket.Entries[I + 1], Bucket.Entries[I],
(Bucket.Count - I) * SizeOf(TJclStrEntry));
Dec(Bucket.Count);
Break;
end;
end;
function TJclStrHashMap.Size: Integer;
begin
Result := FCount;
end;
function TJclStrHashMap.Values: IJclCollection;
var
I, J: Integer;
{$IFDEF THREADSAFE}
CS: IInterface;
{$ENDIF THREADSAFE}
begin
{$IFDEF THREADSAFE}
CS := EnterCriticalSection;
{$ENDIF THREADSAFE}
Result := TJclArrayList.Create(FCapacity, False); // NEVER Owns Objects !
for I := 0 to FCapacity - 1 do
for J := 0 to FBuckets[I].Count - 1 do
Result.Add(FBuckets[I].Entries[J].Value);
end;
//=== { TJclHashMap } ========================================================
constructor TJclHashMap.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, 64);
FHashFunction := HashMul;
end;
destructor TJclHashMap.Destroy;
begin
Clear;
inherited Destroy;
end;
procedure TJclHashMap.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 := nil; // Free key ?
FreeObject(FBuckets[I].Entries[J].Value);
end;
FBuckets[I].Count := 0;
end;
FCount := 0;
end;
function TJclHashMap.Clone: TObject;
var
I, J: Integer;
NewEntryArray: TJclEntryArray;
NewMap: TJclHashMap;
{$IFDEF THREADSAFE}
CS: IInterface;
{$ENDIF THREADSAFE}
begin
{$IFDEF THREADSAFE}
CS := EnterCriticalSection;
{$ENDIF THREADSAFE}
NewMap := TJclHashMap.Create(FCapacity, FOwnsObjects);
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 TJclHashMap.ContainsKey(Key: TObject): Boolean;
var
I: Integer;
Bucket: PJclBucket;
{$IFDEF THREADSAFE}
CS: IInterface;
{$ENDIF THREADSAFE}
begin
{$IFDEF THREADSAFE}
CS := EnterCriticalSection;
{$ENDIF THREADSAFE}
Result := False;
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 := True;
Exit;
end;
end;
function TJclHashMap.ContainsValue(Value: TObject): Boolean;
var
I, J: Integer;
Bucket: PJclBucket;
{$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 TJclHashMap.Equals(AMap: IJclMap): 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;
procedure TJclHashMap.FreeObject(var AObject: TObject);
begin
if FOwnsObjects then
begin
AObject.Free;
AObject := nil;
end;
end;
function TJclHashMap.GetValue(Key: TObject): TObject;
var
I: Integer;
Bucket: PJclBucket;
{$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;
Break;
end;
end;
procedure TJclHashMap.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 TJclHashMap.HashMul(Key: Cardinal): Cardinal;
const
A = 0.6180339887; // (sqrt(5) - 1) / 2
begin
Result := Trunc(FCapacity * (Frac(Key * A)));
//Result := LongRec(Key).Bytes[1] and $FF;
end;
function TJclHashMap.IsEmpty: Boolean;
begin
Result := FCount = 0;
end;
function TJclHashMap.KeySet: IJclSet;
var
I, J: Integer;
{$IFDEF THREADSAFE}
CS: IInterface;
{$ENDIF THREADSAFE}
begin
{$IFDEF THREADSAFE}
CS := EnterCriticalSection;
{$ENDIF THREADSAFE}
Result := TJclArraySet.Create(FCapacity, False); // NEVER Owns Objects !
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 TJclHashMap.PutAll(AMap: IJclMap);
var
It: IJclIterator;
Key: TObject;
{$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 TJclHashMap.PutValue(Key, Value: TObject);
var
Index: Integer;
Bucket: PJclBucket;
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);
begin
Bucket.Entries[Bucket.Count].Key := Key;
Bucket.Entries[Bucket.Count].Value := Value;
end;
Inc(Bucket.Count);
Inc(FCount);
end;
function TJclHashMap.Remove(Key: TObject): TObject;
var
Bucket: PJclBucket;
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
if not FOwnsObjects then
Result := Bucket.Entries[I].Value
else
Bucket.Entries[I].Value.Free;
if I < Length(Bucket.Entries) - 1 then
System.Move(Bucket.Entries[I + 1], Bucket.Entries[I],
(Bucket.Count - I) * SizeOf(TJclEntry));
Dec(Bucket.Count);
Break;
end;
end;
function TJclHashMap.Size: Integer;
begin
Result := FCount;
end;
function TJclHashMap.Values: IJclCollection;
var
I, J: Integer;
{$IFDEF THREADSAFE}
CS: IInterface;
{$ENDIF THREADSAFE}
begin
{$IFDEF THREADSAFE}
CS := EnterCriticalSection;
{$ENDIF THREADSAFE}
Result := TJclArrayList.Create(FCapacity, False); // NEVER Owns Objects !
for I := 0 to FCapacity - 1 do
for J := 0 to FBuckets[I].Count - 1 do
Result.Add(FBuckets[I].Entries[J].Value);
end;
// History:
// $Log: JclHashMaps.pas,v $
// Revision 1.4 2005/03/08 08:33:16 marquardt
// overhaul of exceptions and resourcestrings, minor style cleaning
//
// Revision 1.3 2005/02/27 11:36:20 marquardt
// fixed and secured Capacity/Grow mechanism, raise exceptions with efficient CreateResRec
//
// Revision 1.2 2005/02/27 07:27:47 marquardt
// changed interface names from I to IJcl, moved resourcestrings to JclResource.pas
//
// Revision 1.1 2005/02/24 03:57:10 rrossmair
// - donated DCL code, initial check-in
//
end.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -