📄 jclhashmaps.pas
字号:
{**************************************************************************************************}
{ }
{ Project JEDI Code Library (JCL) }
{ }
{ The contents of this file are subject to the Mozilla Public License Version 1.1 (the "License"); }
{ you may not use this file except in compliance with the License. You may obtain a copy of the }
{ License at http://www.mozilla.org/MPL/ }
{ }
{ Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF }
{ ANY KIND, either express or implied. See the License for the specific language governing rights }
{ and limitations under the License. }
{ }
{ The Original Code is HashMap.pas. }
{ }
{ The Initial Developer of the Original Code is Jean-Philippe BEMPEL aka RDM. Portions created by }
{ Jean-Philippe BEMPEL are Copyright (C) Jean-Philippe BEMPEL (rdm_30 att yahoo dott com) }
{ All rights reserved. }
{ }
{**************************************************************************************************}
{ }
{ The Delphi Container Library }
{ }
{**************************************************************************************************}
// Last modified: $Date: 2005/03/08 08:33:16 $
// For history see end of file
unit JclHashMaps;
{$I jcl.inc}
interface
uses
JclBase, JclAbstractContainers, JclContainerIntf;
type
TJclIntfIntfEntry = record
Key: IInterface;
Value: IInterface;
end;
TJclStrIntfEntry = record
Key: string;
Value: IInterface;
end;
TJclStrStrEntry = record
Key: string;
Value: string;
end;
TJclStrEntry = record
Key: string;
Value: TObject
end;
TJclEntry = record
Key: TObject;
Value: TObject;
end;
TJclIntfIntfEntryArray = array of TJclIntfIntfEntry;
TJclStrIntfEntryArray = array of TJclStrIntfEntry;
TJclStrStrEntryArray = array of TJclStrStrEntry;
TJclStrEntryArray = array of TJclStrEntry;
TJclEntryArray = array of TJclEntry;
PJclIntfIntfBucket = ^TJclIntfIntfBucket;
TJclIntfIntfBucket = record
Count: Integer;
Entries: TJclIntfIntfEntryArray;
end;
PJclStrIntfBucket = ^TJclStrIntfBucket;
TJclStrIntfBucket = record
Count: Integer;
Entries: TJclStrIntfEntryArray;
end;
PJclStrStrBucket = ^TJclStrStrBucket;
TJclStrStrBucket = record
Count: Integer;
Entries: TJclStrStrEntryArray;
end;
PJclStrBucket = ^TJclStrBucket;
TJclStrBucket = record
Count: Integer;
Entries: TJclStrEntryArray;
end;
PJclBucket = ^TJclBucket;
TJclBucket = record
Count: Integer;
Entries: TJclEntryArray;
end;
TJclIntfIntfBucketArray = array of TJclIntfIntfBucket;
TJclStrIntfBucketArray = array of TJclStrIntfBucket;
TJclStrStrBucketArray = array of TJclStrStrBucket;
TJclStrBucketArray = array of TJclStrBucket;
TJclBucketArray = array of TJclBucket;
// Hash Function
TJclHashFunction = function(Key: Cardinal): Cardinal of object;
TJclIntfIntfHashMap = class(TJclAbstractContainer, IJclIntfIntfMap,
IJclIntfCloneable)
private
FCapacity: Integer;
FCount: Integer;
FBuckets: TJclIntfIntfBucketArray;
FHashFunction: TJclHashFunction;
function HashMul(Key: Cardinal): Cardinal;
protected
procedure GrowEntries(BucketIndex: Integer); virtual;
{ IJclIntfIntfMap }
procedure Clear;
function ContainsKey(Key: IInterface): Boolean;
function ContainsValue(Value: IInterface): Boolean;
function Equals(AMap: IJclIntfIntfMap): Boolean;
function GetValue(Key: IInterface): IInterface;
function IsEmpty: Boolean;
function KeySet: IJclIntfSet;
procedure PutAll(AMap: IJclIntfIntfMap);
procedure PutValue(Key, Value: IInterface);
function Remove(Key: IInterface): IInterface;
function Size: Integer;
function Values: IJclIntfCollection;
{ IJclIntfCloneable }
function Clone: IInterface;
public
constructor Create(ACapacity: Integer = DefaultContainerCapacity);
destructor Destroy; override;
property HashFunction: TJclHashFunction read FHashFunction write
FHashFunction;
end;
TJclStrIntfHashMap = class(TJclAbstractContainer, IJclStrIntfMap, IJclIntfCloneable)
private
FCapacity: Integer;
FCount: Integer;
FBuckets: TJclStrIntfBucketArray;
FHashFunction: TJclHashFunction;
function HashMul(Key: Cardinal): Cardinal;
function HashString(const Key: string): Cardinal;
protected
procedure GrowEntries(BucketIndex: Integer); virtual;
{ IJclIntfMap }
procedure Clear;
function ContainsKey(const Key: string): Boolean;
function ContainsValue(Value: IInterface): Boolean;
function Equals(AMap: IJclStrIntfMap): Boolean;
function GetValue(const Key: string): IInterface;
function IsEmpty: Boolean;
function KeySet: IJclStrSet;
procedure PutAll(AMap: IJclStrIntfMap);
procedure PutValue(const Key: string; Value: IInterface);
function Remove(const Key: string): IInterface;
function Size: Integer;
function Values: IJclIntfCollection;
{ IJclIntfCloneable }
function Clone: IInterface;
public
constructor Create(ACapacity: Integer = DefaultContainerCapacity);
destructor Destroy; override;
property HashFunction: TJclHashFunction read FHashFunction write
FHashFunction;
end;
TJclStrStrHashMap = class(TJclAbstractContainer, IJclStrStrMap, IJclIntfCloneable)
private
FCapacity: Integer;
FCount: Integer;
FBuckets: TJclStrStrBucketArray;
FHashFunction: TJclHashFunction;
function HashMul(Key: Cardinal): Cardinal;
function HashString(const Key: string): Cardinal;
protected
procedure GrowEntries(BucketIndex: Integer); virtual;
{ IJclStrStrMap }
procedure Clear;
function ContainsKey(const Key: string): Boolean;
function ContainsValue(const Value: string): Boolean;
function Equals(AMap: IJclStrStrMap): Boolean;
function GetValue(const Key: string): string;
function IsEmpty: Boolean;
function KeySet: IJclStrSet;
procedure PutAll(AMap: IJclStrStrMap);
procedure PutValue(const Key, Value: string);
function Remove(const Key: string): string;
function Size: Integer;
function Values: IJclStrCollection;
// Daniele Teti
function KeyOfValue(const Value: string): string;
{ IJclIntfCloneable }
function Clone: IInterface;
public
constructor Create(ACapacity: Integer = DefaultContainerCapacity);
destructor Destroy; override;
property HashFunction: TJclHashFunction read FHashFunction write
FHashFunction;
end;
TJclStrHashMap = class(TJclAbstractContainer, IJclStrMap, IJclCloneable)
private
FCapacity: Integer;
FCount: Integer;
FBuckets: TJclStrBucketArray;
FHashFunction: TJclHashFunction;
FOwnsObjects: Boolean;
function HashMul(Key: Cardinal): Cardinal;
function HashString(const Key: string): Cardinal;
protected
procedure GrowEntries(BucketIndex: Integer); virtual;
procedure FreeObject(var AObject: TObject);
{ IJclStrMap }
procedure Clear;
function ContainsKey(const Key: string): Boolean;
function ContainsValue(Value: TObject): Boolean;
function Equals(AMap: IJclStrMap): Boolean;
function GetValue(const Key: string): TObject;
function IsEmpty: Boolean;
function KeySet: IJclStrSet;
procedure PutAll(AMap: IJclStrMap);
procedure PutValue(const Key: string; Value: TObject);
function Remove(const Key: string): TObject;
function Size: Integer;
function Values: IJclCollection;
{ IJclCloneable }
function Clone: TObject;
public
constructor Create(ACapacity: Integer = DefaultContainerCapacity;
AOwnsObjects: Boolean = True);
destructor Destroy; override;
property HashFunction: TJclHashFunction read FHashFunction write
FHashFunction;
property OwnsObjects: Boolean read FOwnsObjects;
end;
TJclHashMap = class(TJclAbstractContainer, IJclMap, IJclCloneable)
private
FCapacity: Integer;
FCount: Integer;
FBuckets: TJclBucketArray;
FHashFunction: TJclHashFunction;
FOwnsObjects: Boolean;
function HashMul(Key: Cardinal): Cardinal;
protected
procedure GrowEntries(BucketIndex: Integer); virtual;
procedure FreeObject(var AObject: TObject);
{ IJclCloneable }
function Clone: TObject;
public
constructor Create(ACapacity: Integer = DefaultContainerCapacity;
AOwnsObjects: Boolean = True);
destructor Destroy; override;
{ IJclMap }
procedure Clear;
function ContainsKey(Key: TObject): Boolean;
function ContainsValue(Value: TObject): Boolean;
function Equals(AMap: IJclMap): Boolean;
function GetValue(Key: TObject): TObject;
function IsEmpty: Boolean;
function KeySet: IJclSet;
procedure PutAll(AMap: IJclMap);
procedure PutValue(Key, Value: TObject);
function Remove(Key: TObject): TObject;
function Size: Integer;
function Values: IJclCollection;
property HashFunction: TJclHashFunction read FHashFunction write FHashFunction;
property OwnsObjects: Boolean read FOwnsObjects;
end;
implementation
uses
SysUtils,
JclArrayLists, JclArraySets, JclResources;
//=== { TJclIntfIntfHashMap } ================================================
constructor TJclIntfIntfHashMap.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 TJclIntfIntfHashMap.Destroy;
begin
Clear;
inherited Destroy;
end;
procedure TJclIntfIntfHashMap.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;
FBuckets[I].Entries[J].Value := nil;
end;
FBuckets[I].Count := 0;
end;
FCount := 0;
end;
function TJclIntfIntfHashMap.Clone: IInterface;
var
I, J: Integer;
NewEntryArray: TJclIntfIntfEntryArray;
NewMap: TJclIntfIntfHashMap;
{$IFDEF THREADSAFE}
CS: IInterface;
{$ENDIF THREADSAFE}
begin
{$IFDEF THREADSAFE}
CS := EnterCriticalSection;
{$ENDIF THREADSAFE}
NewMap := TJclIntfIntfHashMap.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 TJclIntfIntfHashMap.ContainsKey(Key: IInterface): Boolean;
var
I: Integer;
Bucket: PJclIntfIntfBucket;
{$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;
Break;
end;
end;
function TJclIntfIntfHashMap.ContainsValue(Value: IInterface): Boolean;
var
I, J: Integer;
Bucket: PJclIntfIntfBucket;
{$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 TJclIntfIntfHashMap.Equals(AMap: IJclIntfIntfMap): 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 TJclIntfIntfHashMap.GetValue(Key: IInterface): IInterface;
var
I: Integer;
Bucket: PJclIntfIntfBucket;
{$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 TJclIntfIntfHashMap.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 TJclIntfIntfHashMap.HashMul(Key: Cardinal): Cardinal;
const
A = 0.6180339887; // (sqrt(5) - 1) / 2
begin
Result := Trunc(FCapacity * (Frac(Key * A)));
end;
function TJclIntfIntfHashMap.IsEmpty: Boolean;
begin
Result := FCount = 0;
end;
function TJclIntfIntfHashMap.KeySet: IJclIntfSet;
var
I, J: Integer;
{$IFDEF THREADSAFE}
CS: IInterface;
{$ENDIF THREADSAFE}
begin
{$IFDEF THREADSAFE}
CS := EnterCriticalSection;
{$ENDIF THREADSAFE}
Result := TJclIntfArraySet.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 TJclIntfIntfHashMap.PutAll(AMap: IJclIntfIntfMap);
var
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -