Solved

Maps, Dynamic arrays

Posted on 2008-06-14
9
1,127 Views
Last Modified: 2013-11-23
Dear Experts,
I want to add objects to an dynamic array, this array will take the key and the object as input, and use the key to get the object.

Ex
// To put objects in the map
map.add(key, myObject)

// To get objects from the map
myObject2 := map.get(key) // return myObject

BR
0
Comment
Question by:engayman01
  • 4
  • 3
  • 2
9 Comments
 
LVL 26

Accepted Solution

by:
Russell Libby earned 100 total points
ID: 21786336
Two things are missing in order to help on this:

1 - Key data type? Depending on what the key data type is (eg if string type), you could use a string list. Or if integer/pointer, you could use a TList to store a record pointer (record of Key / Object) and write custom code for retrieval. Or a dynamic array of a record type could be used.
2 - What is the desired performance (is this time critical), and how many objects will you be storing on average?

There are many acceptable ways to do this (lists/hash/etc), but the key data type and desired speed (linear lookups/hashed/binary splits/etc) will help to determine what will work best for you.

Russell
0
 
LVL 1

Author Comment

by:engayman01
ID: 21787785
Thanks for your reply
1- Its better to be of data type Object, but if it was string there is no problem
2- Time critical, number of objects will be on average less that 300 objects

I think hash will be acceptable, but i need the way to use it :-)

BR
0
 
LVL 4

Assisted Solution

by:tobjectpascal
tobjectpascal earned 25 total points
ID: 21790499

unit Unit1;
 

interface
 

uses

  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,

  StdCtrls;
 

type

  TForm1 = class(TForm)

    Button1: TButton;

    procedure Button1Click(Sender: TObject);

    procedure FormCreate(Sender: TObject);

  private

    { Private declarations }

  public

    { Public declarations }

    YourArray: TStringList;

  end;
 

var

  Form1: TForm1;
 

implementation
 

{$R *.DFM}
 

procedure TForm1.Button1Click(Sender: TObject);

Var

 TheObject: Tobject;

 Position: Integer;

begin

 YourArray.AddObject('Key1',Form1.Button1); //button object

 YourArray.AddObject('Key2',Form1); //Form object

 //.. Add whatever

 //now to find the objects

 Position:=YourArray.IndexOf('Key1');   //Find the object

 TheObject:=YourArray.Objects[Position]; //get the object

 //TheObject now contains the pointer to your object

 TButton(TheObject).Free; //free Button1 or use it for something else

end;
 

procedure TForm1.FormCreate(Sender: TObject);

begin

 YourArray:=TStringList.Create;

 YourArray.Sorted:=True; //important or it will fail.

end;
 

end.

Open in new window

0
 
LVL 26

Expert Comment

by:Russell Libby
ID: 21794937
Example source below which implements a hash which allows for fast lookup. The implementation also allows for indexed retreival of the objects if desired, object owning, and case sensitive/in-sensitive comparions.

Note that the binary lookup of the TStringList is not bad, but can still take up to 9 compares for 300 items.

Example usage:

// uses HashMap
var  objScreen:     TScreen;
     objMouse:      TMouse;
     dwIndex:       Integer;
begin

  // Create with case insensitivty / does not own objects
  with THashMap.Create(False, False) do
  begin
     // Adding items to hash map
     Add('Mouse', Mouse);
     Add('Screen', Screen);
     Add('Form1', Self);
     Add('Application', Application);

     // Using the Find accessor
     if Find('Screen', objScreen) then  ShowMessage(objScreen.ClassName);

     // Using the Get accessor
     objMouse:=TMouse(Get('Mouse'));
     // Need to check assignment
     if Assigned(objMouse) then ShowMessage(objMouse.ClassName);

     // Example using iteration
     for dwIndex:=0 to Pred(Count) do
        ShowMessage(Keys[dwIndex] + '=' + Items[dwIndex].ClassName);

     // Free the hash map
     Free;
  end;

--- source ---
unit HashMap;
////////////////////////////////////////////////////////////////////////////////
//
//   Unit        :  THashMap
//   Author      :  rllibby
//   Date        :  06.16.2008
//   Description :  Object hash that allows for fast lookups, as well as indexed
//                  traversal of the items.
//
////////////////////////////////////////////////////////////////////////////////
interface

////////////////////////////////////////////////////////////////////////////////
//   Include units
////////////////////////////////////////////////////////////////////////////////
uses
  Windows, SysUtils, Classes;

////////////////////////////////////////////////////////////////////////////////
//   Hash constansts
////////////////////////////////////////////////////////////////////////////////
const
  HASH_SIZE         =  521;  // Can be changed, but should always be a prime number

////////////////////////////////////////////////////////////////////////////////
//   THashMapNode
////////////////////////////////////////////////////////////////////////////////
type
  PHashMapNode      =  ^THashMapNode;
  THashMapNode      =  packed record
     Key:           PChar;
     Data:          TObject;
     Next:          PHashMapNode;
  end;

////////////////////////////////////////////////////////////////////////////////
//   String compare prototype
////////////////////////////////////////////////////////////////////////////////
type
  TStrCompare       =  function(const Str1, Str2: PChar): Integer;

////////////////////////////////////////////////////////////////////////////////
//   THashMap
////////////////////////////////////////////////////////////////////////////////
type
  THashMap          =  class(TObject)
  private
     // Private declarations
     FHash:         Array [0..Pred(HASH_SIZE)] of PHashMapNode;
     FList:         TList;
     FCompare:      TStrCompare;
     FCaseSensitive:Boolean;
     FOwnsObjects:  Boolean;
  protected
     // Protected declarations
     function       GetCount: Integer;
     function       GetItems(Index: Integer): TObject;
     function       GetKeys(Index: Integer): String;
     function       HashFunc(Key: PChar): LongWord;
     function       HashCompare(Item1, Item2: PChar): Boolean;
     function       NewNode(Key: PChar; Data: TObject): PHashMapNode;
     procedure      FreeNode(Node: PHashMapNode);
     procedure      SetCaseSensitive(Value: Boolean);
  public
     // Public declarations
     constructor    Create(ACaseSensitive: Boolean = False; AOwnsObjects: Boolean = True);
     destructor     Destroy; override;
     procedure      Clear;
     function       Delete(Key: String): Boolean;
     function       Add(Key: String; Data: TObject): Boolean;
     function       Get(Key: String): TObject;
     function       Find(Key: String; out Data): Boolean;
     function       Extract(Key: String; out Data): Boolean;
  public
     // Public properties
     property       CaseSensitive: Boolean read FCaseSensitive write SetCaseSensitive;
     property       OwnsObjects: Boolean read FOwnsObjects write FOwnsObjects;
     property       Count: Integer read GetCount;
     property       Items[Index: Integer]: TObject read GetItems;
     property       Keys[Index: Integer]: String read GetKeys;
  end;

implementation

//// THashMap //////////////////////////////////////////////////////////////////
constructor THashMap.Create(ACaseSensitive: Boolean = False; AOwnsObjects: Boolean = True);
begin

  // Perform inherited
  inherited Create;

  // Initial values
  FillChar(FHash, SizeOf(FHash), 0);
  FList:=TList.Create;
  FCaseSensitive:=ACaseSensitive;
  FOwnsObjects:=AOwnsObjects;

  // Determine the compare function to use
  if FCaseSensitive then
     // Use StrComp
     @FCompare:=@StrComp
  else
     // Use StrIComp
     @FCompare:=@StrIComp;

end;

destructor THashMap.Destroy;
begin

  // Resource protection
  try
     // Clear the hash
     Clear;
     // Free the list
     FList.Free;
  finally
     // Perform inherited
     inherited Destroy;
  end;

end;

procedure THashMap.Clear;
var  lpNode:        PHashMapNode;
     lpNext:        PHashMapNode;
     dwIndex:       Integer;
begin

  // Resource protection
  try
     // Resource protection
     try
        // Iterate the array and clear the hash nodes
        for dwIndex:=0 to Pred(HASH_SIZE) do
        begin
           // Get bucket node
           lpNode:=FHash[dwIndex];
           // Walk the nodes
           while Assigned(lpNode) do
           begin
              // Get pointer to next item
              lpNext:=lpNode^.Next;
              // Free node
              FreeNode(lpNode);
              // Set iterator to next item
              lpNode:=lpNext;
           end;
        end;
     finally
        // Clear all hash buckets
        FillChar(FHash, SizeOf(FHash), 0);
     end;
  finally
     // Clear the list
     FList.Clear;
  end;

end;

function THashMap.HashFunc(Key: PChar): LongWord;
var  bChar:         Byte;
begin

  // Set starting result
  result:=0;

  // Check key pointer
  if Assigned(Key) then
  begin
     // Generate hash index for key
     while (Key^ > #0) do
     begin
        // Check ascii char
        if (Key^ in ['A'..'Z']) then
           // Lowercase the value
           bChar:=Byte(Key^) + 32
        else
           // Keep value as is
           bChar:=Byte(Key^);
        // Update hash value
        Inc(result, (result shl 3) + bChar);
        // Next char
        Inc(Key);
     end;
  end;

  // Keep result in bounds of array
  result:=result mod HASH_SIZE;

end;

function THashMap.HashCompare(Item1, Item2: PChar): Boolean;
begin

  // Check item1 for null
  if (Item1 = nil) then
     // Check for Item2 being nil
     result:=(Item2 = nil)
  // Check item2 for null
  else if (Item2 = nil) then
     // Item1 is not null, so no possible match
     result:=False
  else
     // Compare the strings
     result:=(FCompare(Item1, Item2) = 0);

end;

function THashMap.NewNode(Key: PChar; Data: TObject): PHashMapNode;
begin

  // Get memory for new node
  GetMem(result, SizeOf(THashMapNode));

  // Resource protection
  try
     // Resource protection
     try
        // Check key
        if Assigned(Key) then
           // Set key and data fields
           result^.Key:=StrCopy(AllocMem(Succ(StrLen(Key))), Key)
        else
           // Allocate byte for null terminator
           result^.Key:=AllocMem(SizeOf(Char));
        // Set data field
        result^.Data:=Data;
     finally
        // Make sure the next node link is cleared
        result^.Next:=nil;
     end;
  finally
     // Add item to list
     FList.Add(result);
  end;

end;

procedure THashMap.FreeNode(Node: PHashMapNode);
begin

  // Remove the node from the list
  FList.Remove(Node);

  // Resource protection
  try
     // Free node key
     FreeMem(Node^.Key);
     // If owns object is true, then free the object
     if FOwnsObjects then Node^.Data.Free;
  finally
     // Free node memory
     FreeMem(Node);
  end;

end;

function THashMap.Extract(Key: String; out Data): Boolean;
var  lpNode:        PHashMapNode;
     lpIter:        PHashMapNode;
     dwIndex:       LongWord;
begin

  // Set default result
  result:=False;

  // Get the hash index
  dwIndex:=HashFunc(Pointer(Key));

  // Check top level bucket
  if Assigned(FHash[dwIndex]) then
  begin
     // Prepare for node iteration
     lpNode:=FHash[dwIndex];
     lpIter:=lpNode;
     // Walk the nodes
     while Assigned(lpIter) do
     begin
        // Match key
        if HashCompare(lpIter^.Key, Pointer(Key)) then break;
        // Save current node
        lpNode:=lpIter;
        // Move to the next node in the chain
        lpIter:=lpNode^.Next;
     end;
     // Check to see if the node is still set
     if Assigned(lpIter) then
     begin
        // Check to see if this is the top level item
        if (lpIter = lpNode) then
           // Link next node into the bucket
           FHash[dwIndex]:=lpIter^.Next
        else
           // Link over this node
           lpNode^.Next:=lpIter^.Next;
        // Set the outbound data
        TObject(Data):=lpIter^.Data;
        // Clear the node data (extract will not free the object)
        lpIter^.Data:=nil;
        // Free the node
        FreeNode(lpIter);
        // Success
        result:=True;
     end;
  end;

end;

function THashMap.Delete(Key: String): Boolean;
var  lpNode:        PHashMapNode;
     lpIter:        PHashMapNode;
     dwIndex:       LongWord;
begin

  // Set default result
  result:=False;

  // Get the hash index
  dwIndex:=HashFunc(Pointer(Key));

  // Check top level bucket
  if Assigned(FHash[dwIndex]) then
  begin
     // Prepare for node iteration
     lpNode:=FHash[dwIndex];
     lpIter:=lpNode;
     // Walk the nodes
     while Assigned(lpIter) do
     begin
        // Match key
        if HashCompare(lpIter^.Key, Pointer(Key)) then break;
        // Save current node
        lpNode:=lpIter;
        // Move to the next node in the chain
        lpIter:=lpNode^.Next;
     end;
     // Check to see if the node is still set
     if Assigned(lpIter) then
     begin
        // Check to see if this is the top level item
        if (lpIter = lpNode) then
           // Link next node into the bucket
           FHash[dwIndex]:=lpIter^.Next
        else
           // Link over this node
           lpNode^.Next:=lpIter^.Next;
        // Free the node
        FreeNode(lpIter);
        // Success
        result:=True;
     end;
  end;

end;

function THashMap.Add(Key: String; Data: TObject): Boolean;
var  lpNode:        PHashMapNode;
     lpIter:        PHashMapNode;
     dwIndex:       LongWord;
begin

  // Get the hash bucket item index
  dwIndex:=HashFunc(Pointer(Key));

  // Resource protection
  try
     // Get the hash bucket item
     lpNode:=FHash[dwIndex];
     // Is the bucket empty?
     if (lpNode = nil) then
        // Add new node into bucket
        FHash[dwIndex]:=NewNode(Pointer(Key), Data)
     else
     begin
        // Save current node
        lpIter:=lpNode;
        // Walk nodes
        while Assigned(lpIter) do
        begin
           // Match the key
           if HashCompare(lpIter^.Key, Pointer(Key)) then
           begin
              // Check for same object
              if not(lpIter^.Data = Data) then
              begin
                 // Not same object, do we own the original (and should we free it)
                 if FOwnsObjects then lpIter^.Data.Free;
                 // Assign new object
                 lpIter^.Data:=Data;
              end;
              // Done processing
              break;
           end;
           // Save current node
           lpNode:=lpIter;
           // Walk next node
           lpIter:=lpNode^.Next;
        end;
        // Do we need to add a new item to the end of the chain?
        if not(Assigned(lpIter)) then
        begin
           // Create new hash node and add to end of the chain
           lpNode^.Next:=NewNode(Pointer(Key), Data);
        end;
     end;
  finally
     // Always success
     result:=True;
  end;

end;

function THashMap.Get(Key: String): TObject;
begin

  // Just another way of calling Find(...)
  if not(Find(Key, result)) then result:=nil;

end;

function THashMap.Find(Key: String; out Data): Boolean;
var  lpNode:        PHashMapNode;
begin

  // Get the hash bucket item
  lpNode:=FHash[HashFunc(Pointer(Key))];

  // Resource protection
  try
     // Walk the items
     while Assigned(lpNode) do
     begin
        // Compare the key
        if HashCompare(lpNode^.Key, Pointer(Key)) then
        begin
           // Key exists, set out return data
           TObject(Data):=lpNode^.Data;
           // Done processing
           break;
        end;
        // Walk the next item
        lpNode:=lpNode^.Next;
     end;
  finally
     // Success if node is assigned
     result:=Assigned(lpNode);
  end;

end;

function THashMap.GetCount: Integer;
begin

  // Return the count of items
  result:=FList.Count;

end;

function THashMap.GetItems(Index: Integer): TObject;
begin

  // Return the data object for the indexed item
  result:=PHashMapNode(FList[Index])^.Data;

end;

function THashMap.GetKeys(Index: Integer): String;
begin

  // Return the Key for the indexed item
  result:=PHashMapNode(FList[Index])^.Key;

end;

procedure THashMap.SetCaseSensitive(Value: Boolean);
begin

  // Set case sensitivity
  if not(FCaseSensitive = Value) then
  begin
     // Change
     FCaseSensitive:=Value;
     // Update the compare function
     if FCaseSensitive then
        // Use StrComp
        @FCompare:=@StrComp
     else
        // Use StrIComp
        @FCompare:=@StrIComp;
  end;

end;

end.
0
How to run any project with ease

Manage projects of all sizes how you want. Great for personal to-do lists, project milestones, team priorities and launch plans.
- Combine task lists, docs, spreadsheets, and chat in one
- View and edit from mobile/offline
- Cut down on emails

 
LVL 4

Expert Comment

by:tobjectpascal
ID: 21799179
how's this (code below) compare in speed to yours rlibby? i'm always on the look out for optimization :)

{$define debug}
unit HashTrie;

{
  Delphi implementation of HashTrie dynamic hashing method
  Full description available on www.softlab.od.ua

  Delphi 2,3,4,5
  Freware with source.

  Copyright (c) 2000-2001, SoftLab MIL-TEC Ltd
  Web:   http://www.softcomplete.com
  Email: support@softcomplete.com

  THIS SOFTWARE AND THE ACCOMPANYING FILES ARE DISTRIBUTED
  "AS IS" AND WITHOUT WARRANTIES AS TO PERFORMANCE OF MERCHANTABILITY OR
  ANY OTHER WARRANTIES WHETHER EXPRESSED OR IMPLIED.
  NO WARRANTY OF FITNESS FOR A PARTICULAR PURPOSE IS OFFERED.
  THE USER MUST ASSUME THE ENTIRE RISK OF USING THE ACCOMPANYING CODE.

  Permission is granted to anyone to use this software for any purpose,
  including commercial applications, and to alter it and redistribute it
  freely, subject to the following restrictions:

  1. The origin of this software must not be misrepresented, you must
     not claim that you wrote the original software. If you use this software
     in a product, an acknowledgment in the product documentation
     would be appreciated but is not required.
  2. Altered source versions must be plainly marked as such, and must not be
     misrepresented as being the original software.
  3. Original copyright may not be removed or altered from any source
     distribution.
  4. All copyright of HashTrie dynamic hashing method belongs to Andre N Belokon,
     SoftLab MIL-TEC Ltd.
}


interface

uses Windows, SysUtils;

const
  // DON'T CHANGE LeafSize VALUE !!! MUST BE EQ 256
  // because some code optimization used
  LeafSize = 256;
  // determines max length of the list
  // very big|small values decrease performance
  // optimum value in range 4..16
  BucketSize = 8;

type
  TLinkedItem = class
  private
    Value: DWORD;
    Data: DWORD;
    Next: TLinkedItem;
    constructor Create(FValue,FData: DWORD; FNext: TLinkedItem);
    destructor Destroy; override;
  end;

  THashTrie = class; // forward
  TTraverseProc = procedure (UserData,UserProc: Pointer;
    Value,Data: DWORD; var Done: Boolean) of object;

  TTreeItem = class
  private
    Owner: THashTrie;
    Level: integer;
    Filled: integer;
    Items: array[0..LeafSize-1] of TObject;
    constructor Create(AOwner: THashTrie);
    destructor Destroy; override;
    function ROR(Value: DWORD): DWORD;
    function RORN(Value: DWORD; Level: integer): DWORD;
    procedure AddDown(Value,Data,Hash: DWORD);
    procedure Delete(Value,Hash: DWORD);
    function Find(Value,Hash: DWORD; var Data: DWORD): Boolean;
    function Traverse(UserData,UserProc: Pointer; TraverseProc: TTraverseProc): Boolean;
  end;

  THashTrie = class
  private
    Root: TTreeItem;
  protected
    function HashValue(Value: DWORD): DWORD; virtual; abstract;
    procedure DestroyItem(var Value,Data: DWORD); virtual; abstract;
    function CompareValue(Value1,Value2: DWORD): Boolean; virtual; abstract;
    procedure AddDown(Value,Data,Hash: DWORD);
    procedure Delete(Value,Hash: DWORD);
    function Find(Value,Hash: DWORD; var Data: DWORD): Boolean;
    procedure Traverse(UserData,UserProc: Pointer; TraverseProc: TTraverseProc);
  public
    constructor Create; virtual;
    destructor Destroy; override;
  end;

  TStrHashTraverseProc = procedure (UserData: Pointer; const Value: string;
    Data: TObject; var Done: Boolean);
  TStrHashTraverseMeth = procedure (UserData: Pointer; const Value: string;
    Data: TObject; var Done: Boolean) of object;

  TStringHashTrie = class(THashTrie)
  private
    FCaseSensitive: Boolean;
    FAutoFreeObjects: Boolean;
  protected
    function HashValue(Value: DWORD): DWORD; override;
    procedure DestroyItem(var Value,Data: DWORD); override;
    function CompareValue(Value1,Value2: DWORD): Boolean; override;
    function HashStr(const S: string): DWORD;
    procedure TraverseProc(UserData,UserProc: Pointer;
      Value,Data: DWORD; var Done: Boolean);
    procedure TraverseMeth(UserData,UserProc: Pointer;
      Value,Data: DWORD; var Done: Boolean);
  public
    Count: Longint;
    constructor Create; override;
    procedure Add(const S: string; Data: TObject);
    procedure Delete(const S: string);
    function Find(const S: string; var Data: TObject): Boolean;
    procedure Traverse(UserData: Pointer; UserProc: TStrHashTraverseProc); overload;
    procedure Traverse(UserData: Pointer; UserProc: TStrHashTraverseMeth); overload;
    property CaseSensitive: Boolean read FCaseSensitive write FCaseSensitive default False;
    property AutoFreeObjects: Boolean read FAutoFreeObjects write FAutoFreeObjects default False;
  end;

function CalcStrCRC32(const S: string): DWORD;
 
{$ifdef debug}
type
  TLenStat = array[1..BucketSize] of integer;

procedure Stat(ht: THashTrie; var MaxLevel, PeakCnt, FillCnt, EmptyCnt: integer;
  var LenStat: TLenStat);
{$endif}

implementation

{$ifdef debug}
procedure Stat(ht: THashTrie; var MaxLevel, PeakCnt, FillCnt, EmptyCnt: integer;
  var LenStat: TLenStat);

  procedure TreeStat(ht: TTreeItem);
  var j,i: integer;
      LinkedItem: TLinkedItem;
  begin
    Inc(PeakCnt);
    if ht.Level+1 > MaxLevel then
      MaxLevel:=ht.Level+1;
    for j:=0 to LeafSize-1 do
      if ht.Items[j] <> nil then begin
        Inc(FillCnt);
        if ht.Items[j] is TTreeItem then begin
          TreeStat(TTreeItem(ht.Items[j]));
        end else begin
          i:=0;
          LinkedItem:=TLinkedItem(ht.Items[j]);
          while LinkedItem <> nil do begin
            Inc(i);
            LinkedItem:=LinkedItem.Next;
          end;
          LenStat[i]:=LenStat[i]+1;
        end;
      end else
        Inc(EmptyCnt);
  end;
begin
  if ht.Root <> nil then
    TreeStat(ht.Root);
end;
{$endif}

{ TTreeItem }

procedure TTreeItem.AddDown(Value, Data, Hash: DWORD);
var i,j: integer;
    TreeItem: TTreeItem;
    LinkedItem: TLinkedItem;
begin
  i:=Hash and $FF;
  if Items[i] = nil then begin
    Items[i]:=TLinkedItem.Create(Value,Data,nil);
    Inc(Filled);
  end else if Items[i] is TTreeItem then begin
    TTreeItem(Items[i]).AddDown(Value,Data,ROR(Hash));
  end else begin
    j:=0;
    LinkedItem:=TLinkedItem(Items[i]);
    while LinkedItem <> nil do begin
      if Owner.CompareValue(LinkedItem.Value,Value) then begin
        // found
        LinkedItem.Data:=Data;
        Exit;
      end;
      LinkedItem:=LinkedItem.Next;
      Inc(j)
    end;
    if j >= BucketSize then begin
      // full
      TreeItem:=TTreeItem.Create(Owner);
      TreeItem.Level:=Level+1;
      LinkedItem:=TLinkedItem(Items[i]);
      while LinkedItem <> nil do begin
        TreeItem.AddDown(LinkedItem.Value, LinkedItem.Data,
                         RORN(Owner.HashValue(LinkedItem.Value), Level+1));
        LinkedItem:=LinkedItem.Next;
      end;
      TreeItem.AddDown(Value,Data,ROR(Hash));
      TLinkedItem(Items[i]).Free;
      Items[i]:=TreeItem;
    end else
      Items[i]:=TLinkedItem.Create(Value,Data,TLinkedItem(Items[i]));
  end;
end;

constructor TTreeItem.Create(AOwner: THashTrie);
var j: integer;
begin
  Owner:=AOwner;
  Level:=0;
  Filled:=0;
  for j:=0 to LeafSize-1 do Items[j]:=nil;
end;

procedure TTreeItem.Delete(Value, Hash: DWORD);
var i: integer;
    TreeItem: TTreeItem;
    PrevLinkedItem,LinkedItem: TLinkedItem;
begin
  i:=Hash and $FF;
  if Items[i] = nil then begin
    Exit;
  end else if Items[i] is TTreeItem then begin
    TTreeItem(Items[i]).Delete(Value,ROR(Hash));
    if TTreeItem(Items[i]).Filled = 0 then begin
      TTreeItem(Items[i]).Free;
      Items[i]:=nil;
    end;
  end else begin
    PrevLinkedItem:=nil;
    LinkedItem:=TLinkedItem(Items[i]);
    while LinkedItem <> nil do begin
      if Owner.CompareValue(LinkedItem.Value,Value) then begin
        // found
        if PrevLinkedItem = nil then begin
          Items[i]:=LinkedItem.Next;
          if Items[i] = nil then
            Dec(Filled);
        end else
          PrevLinkedItem.Next:=LinkedItem.Next;
        LinkedItem.Next:=nil;
        Owner.DestroyItem(LinkedItem.Value,LinkedItem.Data);
        LinkedItem.Free;
        Exit;
      end;
      PrevLinkedItem:=LinkedItem;
      LinkedItem:=LinkedItem.Next;
    end;
  end;
end;

destructor TTreeItem.Destroy;
var j: integer;
    LinkedItem: TLinkedItem;
begin
  for j:=0 to LeafSize-1 do
    if Items[j] <> nil then
      if Items[j] is TTreeItem then
        TTreeItem(Items[j]).Free
      else begin
        LinkedItem:=TLinkedItem(Items[j]);
        while LinkedItem <> nil do begin
          Owner.DestroyItem(LinkedItem.Value,LinkedItem.Data);
          LinkedItem:=LinkedItem.Next;
        end;
        TLinkedItem(Items[j]).Free;
      end;
  inherited;
end;

function TTreeItem.Find(Value, Hash: DWORD; var Data: DWORD): Boolean;
var i: integer;
    TreeItem: TTreeItem;
    LinkedItem: TLinkedItem;
begin
  Result:=False;
  i:=Hash and $FF;
  if Items[i] = nil then begin
    Exit;
  end else if Items[i] is TTreeItem then begin
    Result:=TTreeItem(Items[i]).Find(Value,ROR(Hash),Data);
  end else begin
    LinkedItem:=TLinkedItem(Items[i]);
    while LinkedItem <> nil do begin
      if Owner.CompareValue(LinkedItem.Value,Value) then begin
        // found
        Data:=LinkedItem.Data;
        Result:=True;
        Exit;
      end;
      LinkedItem:=LinkedItem.Next;
    end;
  end;
end;

function TTreeItem.ROR(Value: DWORD): DWORD;
begin
  Result:=((Value and $FF) shl 24) or ((Value shr 8) and $FFFFFF);
end;

function TTreeItem.RORN(Value: DWORD; Level: integer): DWORD;
begin
  Result:=Value;
  while Level > 0 do begin
    Result:=ROR(Result);
    Dec(Level);
  end;
end;

function TTreeItem.Traverse(UserData,UserProc: Pointer;
  TraverseProc: TTraverseProc): Boolean;
var j: integer;
    LinkedItem: TLinkedItem;
begin
  Result:=False;
  for j:=0 to LeafSize-1 do
    if Items[j] <> nil then begin
      if Items[j] is TTreeItem then begin
        Result:=TTreeItem(Items[j]).Traverse(UserData,UserProc,TraverseProc);
      end else begin
        LinkedItem:=TLinkedItem(Items[j]);
        while LinkedItem <> nil do begin
          TraverseProc(UserData,UserProc,LinkedItem.Value,LinkedItem.Data,Result);
          LinkedItem:=LinkedItem.Next;
        end;
      end;
      if Result then Exit;
    end;
end;

{ TLinkedItem }

constructor TLinkedItem.Create(FValue,FData: DWORD; FNext: TLinkedItem);
begin
  Value:=FValue;
  Data:=FData;
  Next:=FNext;
end;

destructor TLinkedItem.Destroy;
begin
  if Next <> nil then
    Next.Free;
end;

{ THashTrie }

procedure THashTrie.AddDown(Value,Data,Hash: DWORD);
begin
  if Root = nil then
    Root:=TTreeItem.Create(Self);
  Root.AddDown(Value,Data,Hash);
end;

procedure THashTrie.Delete(Value,Hash: DWORD);
begin
  if Root <> nil then
    Root.Delete(Value,Hash);
end;

function THashTrie.Find(Value,Hash: DWORD; var Data: DWORD): Boolean;
begin
  if Root <> nil then
    Result:=Root.Find(Value,Hash,Data)
  else
    Result:=False;
end;

constructor THashTrie.Create;
begin
  inherited;
  Root:=nil;
end;

destructor THashTrie.Destroy;
begin
  if Root <> nil then Root.Free;
  inherited;
end;

procedure THashTrie.Traverse(UserData, UserProc: Pointer;
  TraverseProc: TTraverseProc);
begin
  if Root <> nil then
    Root.Traverse(UserData, UserProc, TraverseProc);
end;

{ TStringHashTrie }

procedure TStringHashTrie.Add(const S: string; Data: TObject);
begin
  Inc(Count);
  AddDown(DWORD(NewStr(S)),DWORD(Data),HashStr(S));
end;

function TStringHashTrie.CompareValue(Value1, Value2: DWORD): Boolean;
begin
  if FCaseSensitive then
    Result:=PString(Value1)^ = PString(Value2)^
  else
    Result:=ANSICompareText(PString(Value1)^,PString(Value2)^) = 0;
end;

constructor TStringHashTrie.Create;
begin
  inherited;
  FCaseSensitive:=False;
  FAutoFreeObjects:=False;
end;

procedure TStringHashTrie.Delete(const S: string);
begin
  Dec(Count);
  inherited Delete(DWORD(@S),HashStr(S));
end;

procedure TStringHashTrie.DestroyItem(var Value,Data: DWORD);
begin
  DisposeStr(PString(Value));
  if FAutoFreeObjects then
    TObject(Data).Free;
  Value:=0;
  Data:=0;
end;

function TStringHashTrie.Find(const S: string; var Data: TObject): Boolean;
begin
  Result:=inherited Find(DWORD(@S),HashStr(S),DWORD(Data));
end;

function TStringHashTrie.HashStr(const S: string): DWORD;
{var i: integer;}
begin
  if CaseSensitive then
    Result:=CalcStrCRC32(S)
  else
    Result:=CalcStrCRC32(ANSIUpperCase(S));

{ another hash fucn with good performance
  see code at the end of this unit
  if CaseSensitive then
    Result:=HashP2Str(S)
  else
    Result:=HashP2Str(ANSIUpperCase(S));
}

{ simple hash-func. don't use it !!!
  result:=Length(S);
  for i:=1 to Length(S) do
    if CaseSensitive then
      Result:= ((Result shl 5) xor (Result shr 27)) xor Ord(S[i])
    else
      Result:= ((Result shl 5) xor (Result shr 27)) xor Ord(ANSIUpperCase(S)[i]);
}
end;

function TStringHashTrie.HashValue(Value: DWORD): DWORD;
begin
  Result:=HashStr(PString(Value)^);
end;

procedure TStringHashTrie.Traverse(UserData: Pointer;
  UserProc: TStrHashTraverseProc);
begin
  inherited Traverse(UserData,@UserProc,TraverseProc);
end;

procedure TStringHashTrie.TraverseProc(UserData, UserProc: Pointer; Value,
  Data: DWORD; var Done: Boolean);
begin
  TStrHashTraverseProc(UserProc)(UserData,PString(Value)^,TObject(Data),Done);
end;

procedure TStringHashTrie.Traverse(UserData: Pointer; UserProc: TStrHashTraverseMeth);
begin
  inherited Traverse(UserData,@TMethod(UserProc),TraverseMeth);
end;

procedure TStringHashTrie.TraverseMeth(UserData, UserProc: Pointer; Value,
  Data: DWORD; var Done: Boolean);
type
  PTStrHashTraverseMeth = ^TStrHashTraverseMeth;
begin
  PTStrHashTraverseMeth(UserProc)^(UserData,PString(Value)^,TObject(Data),Done);
end;

{ dynamic crc32 table }

const
  CRC32_POLYNOMIAL = $EDB88320;
var
  Ccitt32Table: array[0..255] of DWORD;

function CalcStrCRC32(const S: string): DWORD;
var j: integer;
begin
  Result:=$FFFFFFFF;
  for j:=1 to Length(S) do
    Result:= (((Result shr 8) and $00FFFFFF) xor (Ccitt32Table[(Result xor byte(S[j])) and $FF]));
end;

procedure BuildCRCTable;
var i, j: longint;
    value: DWORD;
begin
  for i := 0 to 255 do begin
    value := i;
    for j := 8 downto 1 do
      if ((value and 1) <> 0) then
        value := (value shr 1) xor CRC32_POLYNOMIAL
      else
        value := value shr 1;
    Ccitt32Table[i] := value;
  end
end;

{ another hash func with good performance
  but more slow than CRC32

function HashP2(const Buff; buffLen: integer; initval: DWORD): DWORD;
var a,b,c: DWORD;  // the internal state
    len: integer;  // how many key bytes still need mixing
    k: PDWORD;
    kc: PByte;
  procedure hash_mix(var a,b,c: DWORD);
  begin
    a := a-b;  a := a-c;  a := a xor (c shr 13);
    b := b-c;  b := b-a;  b := b xor (a shl 8);
    c := c-a;  c := c-b;  c := c xor (b shr 13);
    a := a-b;  a := a-c;  a := a xor (c shr 12);
    b := b-c;  b := b-a;  b := b xor (a shl 16);
    c := c-a;  c := c-b;  c := c xor (b shr 5);
    a := a-b;  a := a-c;  a := a xor (c shr 3);
    b := b-c;  b := b-a;  b := b xor (a shl 10);
    c := c-a;  c := c-b;  c := c xor (b shr 15);
  end;
begin
   // Set up the internal state
   len := buffLen;
   k := PDWORD(@Buff);
   a := $9E3779B9;  // the golden ratio; an arbitrary value
   b := a;
   c := initval;    // variable initialization of internal state
   //---------------------------------------- handle most of the key
   while len >= 12 do begin
      a:=a+k^; Inc(k);
      b:=b+k^; Inc(k);
      c:=c+k^; Inc(k);
      hash_mix(a,b,c);
      Dec(len,12);
   end;
   //------------------------------------- handle the last 11 bytes
   c := c+DWORD(buffLen);
   kc := PByte(integer(k)+len-1);
   while len > 0 do begin
     case len of  // all the case statements fall through
       11: c := c+(kc^ shl 24);
       10: c := c+(kc^ shl 16);
       9 : c := c+(kc^ shl 8);
        // the first byte of c is reserved for the Len
       8 : b := b+(kc^ shl 24);
       7 : b := b+(kc^ shl 16);
       6 : b := b+(kc^ shl 8);
       5 : b := b+kc^;
       4 : a := a+(kc^ shl 24);
       3 : a := a+(kc^ shl 16);
       2 : a := a+(kc^ shl 8);
       1 : a := a+kc^;
     end;
     Dec(len); Dec(kc);
   end;
   hash_mix(a,b,c);
   //-------------------------------------------- report the result
   Result := c;
end;

function HashP2Str(const Str: string): DWORD;
var S: string;
begin
  S:=ANSIUpperCase(Str);
  Result:=HashP2(S[1],Length(S),0);
end; }

initialization
  BuildCRCTable;
end.


// some code to use the hashtree, it has my crappy code in to time the routines so it's messy (it was designed to test the speed nothing more so just strip everything out except for what's required)

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls, HashTrie;

type
  TForm1 = class(TForm)
    Button2: TButton;
    Memo1: TMemo;
    Edit1: TEdit;
    Label1: TLabel;
    Button1: TButton;
    Button3: TButton;
    Edit2: TEdit;
    Memo2: TMemo;
    Button4: TButton;
    Button5: TButton;
    Button6: TButton;
    Button7: TButton;
    Button8: TButton;
    Button9: TButton;
    Button10: TButton;
    Button11: TButton;
    procedure Button2Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure Button1Click(Sender: TObject);
    procedure Button3Click(Sender: TObject);
    procedure Button6Click(Sender: TObject);
    procedure Button4Click(Sender: TObject);
    procedure Button5Click(Sender: TObject);
    procedure Button7Click(Sender: TObject);
    procedure Button8Click(Sender: TObject);
    procedure Button10Click(Sender: TObject);
    procedure Button9Click(Sender: TObject);
    procedure Button11Click(Sender: TObject);
  private
    ht: TStringHashTrie;
  public
    procedure TraverseMeth(UserData: Pointer; const Value: string;
      Data: TObject; var Done: Boolean);
  end;

var
  Form1: TForm1;
  Sl: TStringList;

implementation

{$R *.DFM}

var
  MaxLevel, PeakCnt, FillCnt, EmptyCnt: integer;
  LenStat: TLenStat;

procedure TraverseProc(UserData: Pointer; const Value: string;
  Data: TObject; var Done: Boolean);
type
  PTextFile = ^TextFile;
begin
  writeln(PTextFile(UserData)^,Value);
end;

procedure TForm1.TraverseMeth(UserData: Pointer; const Value: string;
  Data: TObject; var Done: Boolean);
type
  PTextFile = ^TextFile;
begin
  writeln(PTextFile(UserData)^,Value);
end;

procedure TForm1.Button2Click(Sender: TObject);
var f: TextFile;
    S: string;
    TimeMs,start, stop : Int64;
begin
 QueryPerformanceCounter(start);
  // traverse
  AssignFile(f,'dic.out');
  try
   Rewrite(f);
  //  ht.Traverse(@f,TraverseProc);
   ht.Traverse(@f,TraverseMeth);
  finally
   CloseFile(f);
  end;
 QueryPerformanceCounter(stop);
  Timems := (Stop-Start);
  Memo1.Lines.Add('Time: '+IntToStr(Timems));
end;

procedure TForm1.FormCreate(Sender: TObject);
var f: TextFile;
    S: string;
    j,ipos: integer;
begin
  SL:=TStringList.Create;
  SL.Sorted:=True;
  ht:=TStringHashTrie.Create;
  Ht.CaseSensitive:=True;
  // fill test
 
  // count stats
 { MaxLevel:=0; PeakCnt:=0; FillCnt:=0; EmptyCnt:=0;
  for j:=1 to BucketSize do LenStat[j]:=0;
  Stat(ht, MaxLevel, PeakCnt, FillCnt, EmptyCnt, LenStat);
  Memo1.Lines.Add('MaxLevel: '+IntToStr(MaxLevel));
  Memo1.Lines.Add('PeakCnt: '+IntToStr(PeakCnt));
  Memo1.Lines.Add('FillCnt: '+IntToStr(FillCnt));
  Memo1.Lines.Add('EmptyCnt: '+IntToStr(EmptyCnt));
  for j:=1 to BucketSize do
    Memo1.Lines.Add('Count of Buckets with len='+IntToStr(j)+': '+IntToStr(LenStat[j]));}
Memo1.lines.clear;
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  ht.Free;
end;

procedure TForm1.Button1Click(Sender: TObject);
 var
  Data: TObject;
  TimeMs,start, stop : Int64;
begin
QueryPerformanceCounter(start);
  ht.Find(Edit1.Text,Data);{ then
    Memo1.Lines.Add('String "'+Edit1.Text+'" found. Line No. '+IntToStr(integer(Data)))
  else
    Memo1.Lines.Add('String "'+Edit1.Text+'" not found.');}
  QueryPerformanceCounter(stop);
  Memo1.Lines.Add('String "'+Edit1.Text+'" found. Line No. '+IntToStr(integer(Data)));  
  Timems := (Stop-Start);
  Memo1.Lines.Add('Time: '+IntToStr(Timems));
end;

procedure TForm1.Button3Click(Sender: TObject);
Var
  TimeMs,start, stop : Int64;
begin
 QueryPerformanceCounter(start);
   ht.Add(edit2.text,Tobject(33));
 QueryPerformanceCounter(stop);
 Timems := (Stop-Start);
 Memo1.Lines.Add('Time: '+IntToStr(Timems));
end;

procedure TForm1.Button6Click(Sender: TObject);
Var
  TimeMs,start, stop : Int64;
begin
 QueryPerformanceCounter(start);
 Ht.Delete('ABASH');
 QueryPerformanceCounter(stop);
 Timems := (Stop-Start);
 Memo1.Lines.Add('Time: '+IntToStr(Timems));
end;

procedure TForm1.Button4Click(Sender: TObject);
Var
 Ln: Integer;
 LineNum: Integer;
 TimeMs,start, stop : Int64;
begin
 QueryPerformanceCounter(start);
 //do some code
 Ln:=Sl.IndexOf(Edit1.Text);
 If Ln<>-1 Then
  Begin
    LineNum:=Integer(sl.Objects[Ln]);
 //
  End;
 QueryPerformanceCounter(stop);
  memo2.lines.add('String found '+Sl.Strings[Ln]+' on line '+IntToStr(LineNum));
 Timems := (Stop-Start);
 Memo1.Lines.Add('Time: '+IntToStr(Timems));

end;

procedure TForm1.Button5Click(Sender: TObject);
Var
  TimeMs,start, stop : Int64;
begin
 QueryPerformanceCounter(start);
 Sl.AddObject(edit2.text,Tobject(33));
 QueryPerformanceCounter(stop);
 Timems := (Stop-Start);
 Memo1.Lines.Add('Time: '+IntToStr(Timems));
end;

procedure TForm1.Button7Click(Sender: TObject);
Var
  TimeMs,start, stop : Int64;
begin
 QueryPerformanceCounter(start);
  Sl.SaveToFile('dic.txt');
 QueryPerformanceCounter(stop);
  Timems := (Stop-Start);
 Memo1.Lines.Add('Time: '+IntToStr(Timems));
end;

procedure TForm1.Button8Click(Sender: TObject);
Var
  TimeMs,start, stop : Int64;
begin
 QueryPerformanceCounter(start);
 Sl.Delete(Sl.indexOf('ABASH'));
 QueryPerformanceCounter(stop);
 Timems := (Stop-Start);
 Memo1.Lines.Add('Time: '+IntToStr(Timems));
end;

procedure TForm1.Button10Click(Sender: TObject);
var f: TextFile;
    S: string;
    j,ipos: integer;
    TimeMs,start, stop : Int64;
begin
 QueryPerformanceCounter(start);
 AssignFile(f,'dic.txt');
  try
   Reset(f);
   ipos:=0;
   while not Eof(f) do begin
     readln(f,S);
     ht.Add(S,Tobject(Ipos));
     //Sl.AddObject(S,Tobject(Ipos));
     Inc(ipos);
   end;
  finally
   CloseFile(f);
  end;
  QueryPerformanceCounter(stop);
  Timems := (Stop-Start);
  Memo1.Lines.Add('Time: '+IntToStr(Timems));
end;

procedure TForm1.Button9Click(Sender: TObject);
var f: TextFile;
    S: string;
    j,ipos: integer;
    TimeMs,start, stop : Int64;
begin
 QueryPerformanceCounter(start);
 AssignFile(f,'dic.txt');
  try
   Reset(f);
   ipos:=0;
   while not Eof(f) do begin
     readln(f,S);
     Sl.AddObject(S,Tobject(Ipos));
     Inc(ipos);
   end;
  finally
   CloseFile(f);
  end;
  QueryPerformanceCounter(stop);
  Timems := (Stop-Start);
  Memo1.Lines.Add('Time: '+IntToStr(Timems));
end;

procedure TForm1.Button11Click(Sender: TObject);
var f: TextFile;
    S: string;
    j,ipos: integer;
    TimeMs,start, stop : Int64;
begin
 QueryPerformanceCounter(start);
  Sl.LoadFromFile('dic.txt');
 QueryPerformanceCounter(stop);
  Timems := (Stop-Start);
  Memo1.Lines.Add('Time: '+IntToStr(Timems));
end;

end.
0
 
LVL 26

Expert Comment

by:Russell Libby
ID: 21806783
From what I have tested, the speed of this HashTrie is much slower. Most likely accounted for in the method calls for comparison, extra overhead due to the layout, etc. If the code was restructured, the classes eliminated (into record pointers), and the method calling was reduced, I have a feeling that the speed would be very close.

The code I ran to test is listed below. Not saying that it is a comlpete test (if you want to compare the two, then I leave that up to you).  The HashTrie is a nice addition though, as it does not suffer from the ailments of a straight hash table; that being, you need to know roughly how large of an array to work with, or you run into too many collisions.

-----
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  HashMap, HashTrie, StdCtrls, ExtCtrls, Menus;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Memo1: TMemo;
    CheckBox1: TCheckBox;
    Label1: TLabel;
    MainMenu1: TMainMenu;
    GroupBox1: TGroupBox;
    Panel1: TPanel;
    RadioButton1: TRadioButton;
    Button2: TButton;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

procedure TForm1.Button1Click(Sender: TObject);
var  start, stop, freq:   Int64;
     dwIndex:       Integer;
     objFind:       TObject;
const
  Names: Array [0..7] of String =
        (
           'Button1',
           'Memo1',
           'CheckBox1',
           'Label1',
           'MainMenu1',
           'GroupBox1',
           'Panel1',
           'RadioButton1'
        );

begin

  QueryPerformanceCounter(start);
  try
     with THashMap.Create(False, False) do
     begin
        Add('Button1', Button1);
        Add('Memo1', Memo1);
        Add('CheckBox1', CheckBox1);
        Add('Label1', Label1);
        Add('MainMenu1', MainMenu1);
        Add('GroupBox1', GroupBox1);
        Add('Panel1', Panel1);
        Add('RadioButton1', RadioButton1);
        for dwIndex:=1 to 1000000 do
        begin
           Find(Names[Random(7)], objFind);
        end;
        Free;
     end;
  finally
     QueryPerformanceCounter(stop);
  end;
  QueryPerformanceFrequency(freq);

  Caption:=Format('Time: %n ms', [((stop - start) / freq) * 1000]);

end;

procedure TForm1.Button2Click(Sender: TObject);
var  start, stop, freq:   Int64;
     dwIndex:       Integer;
     objFind:       TObject;
const
  Names: Array [0..7] of String =
        (
           'Button1',
           'Memo1',
           'CheckBox1',
           'Label1',
           'MainMenu1',
           'GroupBox1',
           'Panel1',
           'RadioButton1'
        );

begin

  QueryPerformanceCounter(start);
  try
     with TStringHashTrie.Create do
     begin
        Add('Button1', Button1);
        Add('Memo1', Memo1);
        Add('CheckBox1', CheckBox1);
        Add('Label1', Label1);
        Add('MainMenu1', MainMenu1);
        Add('GroupBox1', GroupBox1);
        Add('Panel1', Panel1);
        Add('RadioButton1', RadioButton1);
        for dwIndex:=1 to 1000000 do
        begin
           Find(Names[Random(7)], objFind);
        end;
        Free;
     end;
  finally
     QueryPerformanceCounter(stop);
  end;
  QueryPerformanceFrequency(freq);

  Caption:=Format('Time: %n ms', [((stop - start) / freq) * 1000]);

end;

end.

--- dfm ---
object Form1: TForm1
  Left = 266
  Top = 214
  Width = 745
  Height = 294
  Caption = 'Form1'
  Color = clBtnFace
  Font.Charset = DEFAULT_CHARSET
  Font.Color = clWindowText
  Font.Height = -11
  Font.Name = 'MS Sans Serif'
  Font.Style = []
  Menu = MainMenu1
  OldCreateOrder = False
  OnCreate = FormCreate
  PixelsPerInch = 96
  TextHeight = 13
  object Label1: TLabel
    Left = 168
    Top = 116
    Width = 32
    Height = 33
    Caption = 'Label1'
  end
  object Button1: TButton
    Left = 12
    Top = 12
    Width = 75
    Height = 25
    Caption = 'Button1'
    TabOrder = 0
    OnClick = Button1Click
  end
  object Memo1: TMemo
    Left = 172
    Top = 20
    Width = 185
    Height = 89
    Lines.Strings = (
      'Memo1')
    TabOrder = 1
  end
  object CheckBox1: TCheckBox
    Left = 404
    Top = 12
    Width = 97
    Height = 17
    Caption = 'CheckBox1'
    TabOrder = 2
  end
  object GroupBox1: TGroupBox
    Left = 308
    Top = 132
    Width = 185
    Height = 105
    Caption = 'GroupBox1'
    TabOrder = 3
  end
  object Panel1: TPanel
    Left = 536
    Top = 48
    Width = 185
    Height = 41
    Caption = 'Panel1'
    TabOrder = 4
  end
  object RadioButton1: TRadioButton
    Left = 184
    Top = 208
    Width = 113
    Height = 17
    Caption = 'RadioButton1'
    TabOrder = 5
  end
  object Button2: TButton
    Left = 16
    Top = 64
    Width = 75
    Height = 25
    Caption = 'Button2'
    TabOrder = 6
    OnClick = Button2Click
  end
  object MainMenu1: TMainMenu
    Left = 220
    Top = 128
  end
end

0
 
LVL 1

Author Comment

by:engayman01
ID: 21810787
From my test The hashmap provides the fastest search time even the number of objects increase, big O(1)

From my search" DCLX is a free library that provides ArrayList, LinkedList, Vector, HashMap, HashSet, ArraySet, Queue and Stack structures "
http://sourceforge.net/projects/dclx


 

0
 
LVL 26

Expert Comment

by:Russell Libby
ID: 21813675
BR,
Do you need further input/assistance on this question?

Russell
0
 
LVL 1

Author Comment

by:engayman01
ID: 21814239
No man, but there was a great info here :-)
0

Featured Post

What Is Threat Intelligence?

Threat intelligence is often discussed, but rarely understood. Starting with a precise definition, along with clear business goals, is essential.

Join & Write a Comment

Suggested Solutions

In this tutorial I will show you how to use the Windows Speech API in Delphi. I will only cover basic functions such as text to speech and controlling the speed of the speech. SAPI Installation First you need to install the SAPI type library, th…
Introduction I have seen many questions in this Delphi topic area where queries in threads are needed or suggested. I know bumped into a similar need. This article will address some of the concepts when dealing with a multithreaded delphi database…
Internet Business Fax to Email Made Easy - With eFax Corporate (http://www.enterprise.efax.com), you'll receive a dedicated online fax number, which is used the same way as a typical analog fax number. You'll receive secure faxes in your email, fr…
You have products, that come in variants and want to set different prices for them? Watch this micro tutorial that describes how to configure prices for Magento super attributes. Assigning simple products to configurable: We assigned simple products…

758 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

19 Experts available now in Live!

Get 1:1 Help Now