+ String and TObject extensions to TFPHashTable

git-svn-id: trunk@4774 -
This commit is contained in:
michael 2006-10-03 09:04:33 +00:00
parent 847377bf79
commit 54cf00e8f5

View File

@ -312,25 +312,23 @@ type
usually via a mod operator; } usually via a mod operator; }
THashFunction = function(const S: string; const TableSize: Longword): Longword; THashFunction = function(const S: string; const TableSize: Longword): Longword;
TIteratorMethod = procedure(Item: Pointer; const Key: string;
var Continue: Boolean) of object;
{ THTNode } { THTNode }
THTNode = class(TObject) THTCustomNode = class(TObject)
private private
FData: pointer;
FKey: string; FKey: string;
public public
constructor CreateWith(const AString: String); constructor CreateWith(const AString: String);
function HasKey(const AKey: string): boolean; function HasKey(const AKey: string): boolean;
property Key: string read FKey; property Key: string read FKey;
property Data: pointer read FData write FData;
end; end;
THTCustomNodeClass = Class of THTCustomNode;
{ TFPHashTable } { TFPCustomHashTable }
TFPHashTable = class(TObject) TFPCustomHashTable = class(TObject)
private private
FHashTable: TFPObjectList; FHashTable: TFPObjectList;
FHashTableSize: Longword; FHashTableSize: Longword;
@ -346,26 +344,24 @@ type
function GetMaxChainLength: Longword; function GetMaxChainLength: Longword;
function Chain(const index: Longword):TFPObjectList; function Chain(const index: Longword):TFPObjectList;
protected protected
Function CreateNewNode(const aKey : string) : THTCustomNode; virtual; abstract;
Procedure AddNode(ANode : THTCustomNode); virtual; abstract;
function ChainLength(const ChainIndex: Longword): Longword; virtual; function ChainLength(const ChainIndex: Longword): Longword; virtual;
procedure SetData(const index: string; const AValue: Pointer); virtual; function FindOrCreateNew(const aKey: string): THTCustomNode; virtual;
function GetData(const index: string):Pointer; virtual;
function FindOrCreateNew(const aKey: string): THTNode; virtual;
function ForEachCall(aMethod: TIteratorMethod): THTNode; virtual;
procedure SetHashFunction(AHashFunction: THashFunction); virtual; procedure SetHashFunction(AHashFunction: THashFunction); virtual;
Function FindChainForAdd(Const aKey : String) : TFPObjectList;
public public
constructor Create; constructor Create;
constructor CreateWith(AHashTableSize: Longword; aHashFunc: THashFunction); constructor CreateWith(AHashTableSize: Longword; aHashFunc: THashFunction);
destructor Destroy; override; destructor Destroy; override;
procedure ChangeTableSize(const ANewSize: Longword); virtual; procedure ChangeTableSize(const ANewSize: Longword); virtual;
procedure Clear; virtual; procedure Clear; virtual;
procedure Add(const aKey: string; AItem: pointer); virtual;
procedure Delete(const aKey: string); virtual; procedure Delete(const aKey: string); virtual;
function Find(const aKey: string): THTNode; function Find(const aKey: string): THTCustomNode;
function IsEmpty: boolean; function IsEmpty: boolean;
property HashFunction: THashFunction read FHashFunction write SetHashFunction; property HashFunction: THashFunction read FHashFunction write SetHashFunction;
property Count: Longword read FCount; property Count: Longword read FCount;
property HashTableSize: Longword read FHashTableSize write SetHashTableSize; property HashTableSize: Longword read FHashTableSize write SetHashTableSize;
property Items[const index: string]: Pointer read GetData write SetData; default;
property HashTable: TFPObjectList read FHashTable; property HashTable: TFPObjectList read FHashTable;
property VoidSlots: Longword read GetVoidSlots; property VoidSlots: Longword read GetVoidSlots;
property LoadFactor: double read GetLoadFactor; property LoadFactor: double read GetLoadFactor;
@ -375,6 +371,88 @@ type
property Density: Longword read GetDensity; property Density: Longword read GetDensity;
end; end;
{ TFPDataHashTable : Hash table with simple data pointers }
THTDataNode = Class(THTCustomNode)
Private
FData: pointer;
public
property Data: pointer read FData write FData;
end;
// For compatibility
THTNode = THTDataNode;
TDataIteratorMethod = procedure(Item: Pointer; const Key: string; var Continue: Boolean) of object;
// For compatibility
TIteratorMethod = TDataIteratorMethod;
TFPDataHashTable = Class(TFPCustomHashTable)
Protected
Function CreateNewNode(const aKey : String) : THTCustomNode; override;
Procedure AddNode(ANode : THTCustomNode); override;
procedure SetData(const index: string; const AValue: Pointer); virtual;
function GetData(const index: string):Pointer; virtual;
function ForEachCall(aMethod: TDataIteratorMethod): THTDataNode; virtual;
Public
procedure Add(const aKey: string; AItem: pointer); virtual;
property Items[const index: string]: Pointer read GetData write SetData; default;
end;
{ TFPStringHashTable : Hash table with simple strings as data }
THTStringNode = Class(THTCustomNode)
Private
FData : String;
public
property Data: String read FData write FData;
end;
TStringIteratorMethod = procedure(Item: String; const Key: string; var Continue: Boolean) of object;
TFPStringHashTable = Class(TFPCustomHashTable)
Protected
Function CreateNewNode(const aKey : String) : THTCustomNode; override;
Procedure AddNode(ANode : THTCustomNode); override;
procedure SetData(const Index, AValue: string); virtual;
function GetData(const index: string): String; virtual;
function ForEachCall(aMethod: TStringIteratorMethod): THTStringNode; virtual;
Public
procedure Add(const aKey,aItem: string); virtual;
property Items[const index: string]: String read GetData write SetData; default;
end;
{ TFPStringHashTable : Hash table with simple strings as data }
THTObjectNode = Class(THTCustomNode)
Private
FData : TObject;
public
property Data: TObject read FData write FData;
end;
THTOwnedObjectNode = Class(THTObjectNode)
public
Destructor Destroy; override;
end;
TObjectIteratorMethod = procedure(Item: TObject; const Key: string; var Continue: Boolean) of object;
TFPObjectHashTable = Class(TFPCustomHashTable)
Private
FOwnsObjects : Boolean;
Protected
Function CreateNewNode(const aKey : String) : THTCustomNode; override;
Procedure AddNode(ANode : THTCustomNode); override;
procedure SetData(const Index: string; AObject : TObject); virtual;
function GetData(const index: string): TObject; virtual;
function ForEachCall(aMethod: TObjectIteratorMethod): THTObjectNode; virtual;
Public
constructor Create(AOwnsObjects : Boolean = True);
constructor CreateWith(AHashTableSize: Longword; aHashFunc: THashFunction; AOwnsObjects : Boolean = True);
procedure Add(const aKey: string; AItem : TObject); virtual;
property Items[const index: string]: TObject read GetData write SetData; default;
Property OwnsObjects : Boolean Read FOwnsObjects Write FOwnsObjects;
end;
EDuplicate = class(Exception); EDuplicate = class(Exception);
EKeyNotFound = class(Exception); EKeyNotFound = class(Exception);
@ -1566,13 +1644,13 @@ end;
{ THTNode } { THTNode }
constructor THTNode.CreateWith(const AString: string); constructor THTCustomNode.CreateWith(const AString: string);
begin begin
inherited Create; inherited Create;
FKey := AString; FKey := AString;
end; end;
function THTNode.HasKey(const AKey: string): boolean; function THTCustomNode.HasKey(const AKey: string): boolean;
begin begin
if Length(AKey) <> Length(FKey) then if Length(AKey) <> Length(FKey) then
begin begin
@ -1583,17 +1661,14 @@ begin
Result := CompareMem(PChar(FKey), PChar(AKey), length(AKey)); Result := CompareMem(PChar(FKey), PChar(AKey), length(AKey));
end; end;
{ TFPHashTable } { TFPCustomHashTable }
constructor TFPHashTable.Create; constructor TFPCustomHashTable.Create;
begin begin
Inherited Create; CreateWith(196613,@RSHash);
FHashTable := TFPObjectList.Create(True);
HashTableSize := 196613;
FHashFunction := @RSHash;
end; end;
constructor TFPHashTable.CreateWith(AHashTableSize: Longword; constructor TFPCustomHashTable.CreateWith(AHashTableSize: Longword;
aHashFunc: THashFunction); aHashFunc: THashFunction);
begin begin
Inherited Create; Inherited Create;
@ -1602,28 +1677,23 @@ begin
FHashFunction := aHashFunc; FHashFunction := aHashFunc;
end; end;
destructor TFPHashTable.Destroy; destructor TFPCustomHashTable.Destroy;
begin begin
FHashTable.Free; FHashTable.Free;
inherited Destroy; inherited Destroy;
end; end;
function TFPHashTable.GetDensity: Longword; function TFPCustomHashTable.GetDensity: Longword;
begin begin
Result := FHashTableSize - VoidSlots Result := FHashTableSize - VoidSlots
end; end;
function TFPHashTable.GetNumberOfCollisions: Longword; function TFPCustomHashTable.GetNumberOfCollisions: Longword;
begin begin
Result := FCount -(FHashTableSize - VoidSlots) Result := FCount -(FHashTableSize - VoidSlots)
end; end;
procedure TFPHashTable.SetData(const index: string; const AValue: Pointer); procedure TFPCustomHashTable.SetHashTableSize(const Value: Longword);
begin
FindOrCreateNew(index).Data := AValue;
end;
procedure TFPHashTable.SetHashTableSize(const Value: Longword);
var var
i: Longword; i: Longword;
newSize: Longword; newSize: Longword;
@ -1644,7 +1714,7 @@ begin
end; end;
end; end;
procedure TFPHashTable.InitializeHashTable; procedure TFPCustomHashTable.InitializeHashTable;
var var
i: LongWord; i: LongWord;
begin begin
@ -1654,12 +1724,12 @@ begin
FCount := 0; FCount := 0;
end; end;
procedure TFPHashTable.ChangeTableSize(const ANewSize: Longword); procedure TFPCustomHashTable.ChangeTableSize(const ANewSize: Longword);
var var
SavedTable: TFPObjectList; SavedTable: TFPObjectList;
SavedTableSize: Longword; SavedTableSize: Longword;
i, j: Longword; i, j: Longword;
temp: THTNode; temp: THTCustomNode;
begin begin
SavedTable := FHashTable; SavedTable := FHashTable;
SavedTableSize := FHashTableSize; SavedTableSize := FHashTableSize;
@ -1672,14 +1742,14 @@ begin
if Assigned(SavedTable[i]) then if Assigned(SavedTable[i]) then
for j := 0 to TFPObjectList(SavedTable[i]).Count -1 do for j := 0 to TFPObjectList(SavedTable[i]).Count -1 do
begin begin
temp := THTNode(TFPObjectList(SavedTable[i])[j]); temp := THTCustomNode(TFPObjectList(SavedTable[i])[j]);
Add(temp.Key, temp.Data); AddNode(temp);
end; end;
end; end;
SavedTable.Free; SavedTable.Free;
end; end;
procedure TFPHashTable.SetHashFunction(AHashFunction: THashFunction); procedure TFPCustomHashTable.SetHashFunction(AHashFunction: THashFunction);
begin begin
if IsEmpty then if IsEmpty then
FHashFunction := AHashFunction FHashFunction := AHashFunction
@ -1687,7 +1757,7 @@ begin
raise Exception.Create(NotEmptyMsg); raise Exception.Create(NotEmptyMsg);
end; end;
function TFPHashTable.Find(const aKey: string): THTNode; function TFPCustomHashTable.Find(const aKey: string): THTCustomNode;
var var
hashCode: Longword; hashCode: Longword;
chn: TFPObjectList; chn: TFPObjectList;
@ -1699,27 +1769,107 @@ begin
begin begin
if chn.count>0 then if chn.count>0 then
for i := 0 to chn.Count - 1 do for i := 0 to chn.Count - 1 do
if THTNode(chn[i]).HasKey(aKey) then if THTCustomNode(chn[i]).HasKey(aKey) then
begin begin
result := THTNode(chn[i]); result := THTCustomNode(chn[i]);
exit; exit;
end; end;
end; end;
Result := nil; Result := nil;
end; end;
function TFPHashTable.GetData(const Index: string): Pointer; Function TFPCustomHashTable.FindChainForAdd(Const aKey : String) : TFPObjectList;
var var
node: THTNode; hashCode: Longword;
i: Longword;
begin begin
node := Find(Index); hashCode := FHashFunction(aKey, FHashTableSize);
if Assigned(node) then Result := Chain(hashCode);
Result := node.Data if Assigned(Result) then
begin
if Result.count>0 then
for i := 0 to Result.Count - 1 do
if THTCustomNode(Result[i]).HasKey(aKey) then
Raise EDuplicate.CreateFmt(DuplicateMsg, [aKey]);
end
else else
Result := nil; begin
FHashTable[hashcode] := TFPObjectList.Create(true);
Result := Chain(hashcode);
end;
inc(FCount);
end; end;
function TFPHashTable.FindOrCreateNew(const aKey: string): THTNode;
procedure TFPCustomHashTable.Delete(const aKey: string);
var
hashCode: Longword;
chn: TFPObjectList;
i: Longword;
begin
hashCode := FHashFunction(aKey, FHashTableSize);
chn := Chain(hashCode);
if Assigned(chn) then
begin
if chn.count>0 then
for i := 0 to chn.Count - 1 do
if THTCustomNode(chn[i]).HasKey(aKey) then
begin
chn.Delete(i);
dec(FCount);
exit;
end;
end;
raise EKeyNotFound.CreateFmt(KeyNotFoundMsg, ['Delete', aKey]);
end;
function TFPCustomHashTable.IsEmpty: boolean;
begin
Result := (FCount = 0);
end;
function TFPCustomHashTable.Chain(const index: Longword): TFPObjectList;
begin
Result := TFPObjectList(FHashTable[index]);
end;
function TFPCustomHashTable.GetVoidSlots: Longword;
var
i: Longword;
num: Longword;
begin
num := 0;
if FHashTableSize>0 Then
for i:= 0 to FHashTableSize-1 do
if Not Assigned(Chain(i)) then
inc(num);
result := num;
end;
function TFPCustomHashTable.GetLoadFactor: double;
begin
Result := Count / FHashTableSize;
end;
function TFPCustomHashTable.GetAVGChainLen: double;
begin
result := Count / (FHashTableSize - VoidSlots);
end;
function TFPCustomHashTable.GetMaxChainLength: Longword;
var
i: Longword;
begin
Result := 0;
if FHashTableSize>0 Then
for i := 0 to FHashTableSize-1 do
if ChainLength(i) > Result then
Result := ChainLength(i);
end;
function TFPCustomHashTable.FindOrCreateNew(const aKey: string): THTCustomNode;
var var
hashCode: Longword; hashCode: Longword;
chn: TFPObjectList; chn: TFPObjectList;
@ -1731,7 +1881,7 @@ begin
begin begin
if chn.count>0 then if chn.count>0 then
for i := 0 to chn.Count - 1 do for i := 0 to chn.Count - 1 do
if THTNode(chn[i]).HasKey(aKey) then if THTCustomNode(chn[i]).HasKey(aKey) then
begin begin
Result := THTNode(chn[i]); Result := THTNode(chn[i]);
exit; exit;
@ -1743,11 +1893,11 @@ begin
chn := Chain(hashcode); chn := Chain(hashcode);
end; end;
inc(FCount); inc(FCount);
Result := THTNode.CreateWith(aKey); Result := CreateNewNode(aKey);
chn.Add(Result); chn.Add(Result);
end; end;
function TFPHashTable.ChainLength(const ChainIndex: Longword): Longword; function TFPCustomHashTable.ChainLength(const ChainIndex: Longword): Longword;
begin begin
if Assigned(Chain(ChainIndex)) then if Assigned(Chain(ChainIndex)) then
Result := Chain(ChainIndex).Count Result := Chain(ChainIndex).Count
@ -1755,7 +1905,7 @@ begin
Result := 0; Result := 0;
end; end;
procedure TFPHashTable.Clear; procedure TFPCustomHashTable.Clear;
var var
i: Longword; i: Longword;
begin begin
@ -1768,7 +1918,44 @@ begin
FCount := 0; FCount := 0;
end; end;
function TFPHashTable.ForEachCall(aMethod: TIteratorMethod): THTNode;
{ TFPDataHashTable }
procedure TFPDataHashTable.Add(const aKey: string; aItem: pointer);
var
chn: TFPObjectList;
NewNode: THtDataNode;
begin
chn:=FindChainForAdd(akey);
NewNode := THtDataNode(CreateNewNode(aKey));
NewNode.Data := aItem;
chn.Add(NewNode);
end;
function TFPDataHashTable.GetData(const Index: string): Pointer;
var
node: THTDataNode;
begin
node := THTDataNode(Find(Index));
if Assigned(node) then
Result := node.Data
else
Result := nil;
end;
procedure TFPDataHashTable.SetData(const index: string; const AValue: Pointer);
begin
THTDataNode(FindOrCreateNew(index)).Data := AValue;
end;
Function TFPDataHashTable.CreateNewNode(const aKey : string) : THTCustomNode;
begin
Result:=THTDataNode.CreateWith(aKey);
end;
function TFPDataHashTable.ForEachCall(aMethod: TDataIteratorMethod): THTDataNode;
var var
i, j: Longword; i, j: Longword;
continue: boolean; continue: boolean;
@ -1783,10 +1970,10 @@ begin
if chain(i).count>0 then if chain(i).count>0 then
for j := 0 to Chain(i).Count-1 do for j := 0 to Chain(i).Count-1 do
begin begin
aMethod(THTNode(Chain(i)[j]).Data, THTNode(Chain(i)[j]).Key, continue); aMethod(THTDataNode(Chain(i)[j]).Data, THTDataNode(Chain(i)[j]).Key, continue);
if not continue then if not continue then
begin begin
Result := THTNode(Chain(i)[j]); Result := THTDataNode(Chain(i)[j]);
Exit; Exit;
end; end;
end; end;
@ -1794,97 +1981,175 @@ begin
end; end;
end; end;
procedure TFPHashTable.Add(const aKey: string; aItem: pointer); Procedure TFPDataHashTable.AddNode(ANode : THTCustomNode);
var
hashCode: Longword;
chn: TFPObjectList;
i: Longword;
NewNode: THtNode;
begin begin
hashCode := FHashFunction(aKey, FHashTableSize); With THTDataNode(ANode) do
chn := Chain(hashCode); Add(Key,Data);
if Assigned(chn) then end;
begin
if chn.count>0 then { TFPStringHashTable }
for i := 0 to chn.Count - 1 do
if THTNode(chn[i]).HasKey(aKey) then Procedure TFPStringHashTable.AddNode(ANode : THTCustomNode);
Raise EDuplicate.CreateFmt(DuplicateMsg, [aKey]);
end begin
With THTStringNode(ANode) do
Add(Key,Data);
end;
function TFPStringHashTable.GetData(const Index: string): String;
var
node: THTStringNode;
begin
node := THTStringNode(Find(Index));
if Assigned(node) then
Result := node.Data
else else
begin Result := '';
FHashTable[hashcode] := TFPObjectList.Create(true); end;
chn := Chain(hashcode);
end; procedure TFPStringHashTable.SetData(const index, AValue: string);
inc(FCount); begin
NewNode := THTNode.CreateWith(aKey); THTStringNode(FindOrCreateNew(index)).Data := AValue;
end;
procedure TFPStringHashTable.Add(const aKey, aItem: string);
var
chn: TFPObjectList;
NewNode: THtStringNode;
begin
chn:=FindChainForAdd(akey);
NewNode := THtStringNode(CreateNewNode(aKey));
NewNode.Data := aItem; NewNode.Data := aItem;
chn.Add(NewNode); chn.Add(NewNode);
end; end;
procedure TFPHashTable.Delete(const aKey: string); Function TFPStringHashTable.CreateNewNode(const aKey : string) : THTCustomNode;
begin
Result:=THTStringNode.CreateWith(aKey);
end;
function TFPStringHashTable.ForEachCall(aMethod: TStringIteratorMethod): THTStringNode;
var var
hashCode: Longword; i, j: Longword;
chn: TFPObjectList; continue: boolean;
i: Longword;
begin begin
hashCode := FHashFunction(aKey, FHashTableSize); Result := nil;
chn := Chain(hashCode); continue := true;
if Assigned(chn) then if FHashTableSize>0 then
begin
if chn.count>0 then
for i := 0 to chn.Count - 1 do
if THTNode(chn[i]).HasKey(aKey) then
begin
chn.Delete(i);
dec(FCount);
exit;
end;
end;
raise EKeyNotFound.CreateFmt(KeyNotFoundMsg, ['Delete', aKey]);
end;
function TFPHashTable.IsEmpty: boolean;
begin
Result := (FCount = 0);
end;
function TFPHashTable.Chain(const index: Longword): TFPObjectList;
begin
Result := TFPObjectList(FHashTable[index]);
end;
function TFPHashTable.GetVoidSlots: Longword;
var
i: Longword;
num: Longword;
begin
num := 0;
if FHashTableSize>0 Then
for i:= 0 to FHashTableSize-1 do
if Not Assigned(Chain(i)) then
inc(num);
result := num;
end;
function TFPHashTable.GetLoadFactor: double;
begin
Result := Count / FHashTableSize;
end;
function TFPHashTable.GetAVGChainLen: double;
begin
result := Count / (FHashTableSize - VoidSlots);
end;
function TFPHashTable.GetMaxChainLength: Longword;
var
i: Longword;
begin
Result := 0;
if FHashTableSize>0 Then
for i := 0 to FHashTableSize-1 do for i := 0 to FHashTableSize-1 do
if ChainLength(i) > Result then begin
Result := ChainLength(i); if assigned(Chain(i)) then
begin
if chain(i).count>0 then
for j := 0 to Chain(i).Count-1 do
begin
aMethod(THTStringNode(Chain(i)[j]).Data, THTStringNode(Chain(i)[j]).Key, continue);
if not continue then
begin
Result := THTStringNode(Chain(i)[j]);
Exit;
end;
end;
end;
end;
end; end;
{ TFPObjectHashTable }
Procedure TFPObjectHashTable.AddNode(ANode : THTCustomNode);
begin
With THTObjectNode(ANode) do
Add(Key,Data);
end;
function TFPObjectHashTable.GetData(const Index: string): TObject;
var
node: THTObjectNode;
begin
node := THTObjectNode(Find(Index));
if Assigned(node) then
Result := node.Data
else
Result := Nil;
end;
procedure TFPObjectHashTable.SetData(const index : string; AObject : TObject);
begin
THTObjectNode(FindOrCreateNew(index)).Data := AObject;
end;
procedure TFPObjectHashTable.Add(const aKey: string; AItem : TObject);
var
chn: TFPObjectList;
NewNode: THTObjectNode;
begin
chn:=FindChainForAdd(akey);
NewNode := THTObjectNode(CreateNewNode(aKey));
NewNode.Data := aItem;
chn.Add(NewNode);
end;
Function TFPObjectHashTable.CreateNewNode(const aKey : string) : THTCustomNode;
begin
If OwnsObjects then
Result:=THTOwnedObjectNode.CreateWith(aKey)
else
Result:=THTObjectNode.CreateWith(aKey);
end;
function TFPObjectHashTable.ForEachCall(aMethod: TObjectIteratorMethod): THTObjectNode;
var
i, j: Longword;
continue: boolean;
begin
Result := nil;
continue := true;
if FHashTableSize>0 then
for i := 0 to FHashTableSize-1 do
begin
if assigned(Chain(i)) then
begin
if chain(i).count>0 then
for j := 0 to Chain(i).Count-1 do
begin
aMethod(THTObjectNode(Chain(i)[j]).Data, THTObjectNode(Chain(i)[j]).Key, continue);
if not continue then
begin
Result := THTObjectNode(Chain(i)[j]);
Exit;
end;
end;
end;
end;
end;
constructor TFPObjectHashTable.Create(AOwnsObjects : Boolean = True);
begin
Inherited Create;
FOwnsObjects:=AOwnsObjects;
end;
constructor TFPObjectHashTable.CreateWith(AHashTableSize: Longword; aHashFunc: THashFunction; AOwnsObjects : Boolean = True);
begin
Inherited CreateWith(AHashTableSize,AHashFunc);
FOwnsObjects:=AOwnsObjects;
end;
Destructor THTOwnedObjectNode.Destroy;
begin
FreeAndNil(FData);
Inherited;
end;
end. end.