 
		
		9
		
			 
		
	 		
	Tags:   
	
			delphi
			
	
		
	
	
	
	
	
		 
		
			Skrevet af 
Bruger #489
			 @ 23.09.2002 
		 	
	HashTabel i Delphi
	Motivation:
Hvis man arbejder med store mængder data i hukommelsen er det ofte
nødvendigt at pålægge dem en form for orden og struktur. Arrays,
lister, træer, stakke er alle eksempler herpå. Når man vælger hvilken
metode man vil benytte er der flere ting der skal overvejes,
f.eks. hvor hurtigt man kan hente/indsætte elementer, samt hvilken
type data der skal opbevares.
De ovenstående datastrukturer har alle fordele og ulemper, som
jeg ikke vil komme nærmere ind på her. I stedet vil jeg give et
eksempel på hvordan man i Delphi kan konstruere en Hashtabel, som
kan indeholde et vilkårligt element, så længe elementet har en
unik nøgle tilknyttet, og tilgå/indsætte disse på en effektiv måde.
Mit mål her er kun at beskrive idéen bag en hashtabel, ikke at gå
i detaljer med hvordan den implementeres. Til sidst i teksten
er der dog et eksempel på en hashtabel, baseret på nedenstående idé.
Hashen:
En hash-funktion er en funktion der som input tager en tekststreng, og
som output genererer en nøgle. Funktionen er "many-to-one", hvilket vil
sige at flere strenge godt kan give den samme nøgle. Desto færre gange
den samme nøgle bliver genereret ud fra forskellige strenge, desto bedre.
Tabellen:
Tabellen består af et array af lister. Listerne indholder elementerne
i tabellen. Tabellens funktionalitet illustreres nok nemmest med et eksempel.
Lad os sige vi ønsker at indsætte superhelte. Allerførst skal vi bestemme
os for hvilken streng vi skal sende til hashfunktionen. Strengen skal kunne
identificerer den pågældende superhelt unikt, så vi vælger hans heltenavn
(supermand, batman osv.). Hash-funktionen genererer nu en integer. Denne
integer benytter vi til at indeksere ind i arrayet af lister. Såfremt
vores superhelt ikke allerede eksisterer i listen, bliver han tilføjet.
Vi har nu en struktur som ligner denne:
[0]-list|supermand|
[1]-list|
[2]-list|batman|robin|
[3]-list|hulk|
[4]-null
[5]-null
Arrayet størrelse er en parameter til hashtabellens konstruktør.
Når så et element skal hentes ud igen er fremgangsmåden den samme, blot
skal vi søge igennem den pågældende liste, indtil den rigtige superhelt
er fundet.
Det er her det er nødvendigt at hashfunktionen fordeler nøglerne så ligeligt
som overhovedet muligt, ellers bliver listerne for store, hvilket resulterer
i at der er meget at søge igennem før det rigtige element er fundet. I værste
tilfælde alle elmenterne (sker aldrig med mange elementer og god hash).
Når hash-funktionen fungerer fornuftigt, vil man tilgengæld opnå en effektivitet
næsten på højde med at indeksere i et array.
Jeg har også tilføjet en sorteringsfunktion. 
Da hash-tabellen ikke har noget som helst kendskab til de elementer den indeholder er det nødvendigt at sende en sammenligningsfunktion med som argument.
unit utils;
interface
uses
  Classes,
  CUser;
{Would propably be more effective to implement my own list, but this will do}
type PTList = ^TList;
type
  PElement = ^Element;
  Element = class(TObject)
    private
      key : string;
      e : Pointer;
    public
      Constructor Create(key : string; e : Pointer);
      function getKey() : string;
      function getElement() : Pointer;
  end;
{Declaration of type HashTable. It has an array of list's}
type
  HashTable = class(TObject)
    private
      table: array of Tlist;
      x : array of integer; //Used by hash function
      tableSize : integer;
      function getHash(key : string): integer;
      procedure merge(pl : PTList);
    public
      Constructor Create(tableSize: integer);
      procedure Insert(key : string; item : Pointer);
      function Get(key : string) : Pointer;
      function Sort(Compare : TListSortCompare) : PTList;
  end;
implementation
{##############################type element###########################################}
{Public functions}
Constructor Element.Create(key : string; e : Pointer);
  begin
    Self.key := key;
    Self.e := e;
  end;
function Element.getKey() : string;
  begin
    result := key;
  end;
function Element.getElement() : Pointer;
  begin
    result := e;
  end;
{##############################type hashTable###########################################}
{Public functions}
Constructor HashTable.Create(tableSize: integer);
  var
    k, s, i, j : integer;
  begin
    SetLength(table, tableSize);
    Self.tableSize := tableSize - 1;
    {Initializing the hash function}
    SetLength(x, 256);
    for i := 0 to 255 do
      begin
        x[i] := i;
      end;
    k := 7;
    for j := 0 to 3 do
      begin
        for i := 0 to 255 do
          begin
            s := x[i];
            k := (k + s) mod 256;
            x[i] := x[k];
            x[k] := s;
          end;
      end;
  end;
procedure HashTable.Insert(key : string; item : Pointer);
  var
    tableIndex : integer;
    pe : PElement;
  begin
    tableIndex := getHash(key);
    if table[tableIndex] = nil Then table[tableIndex] := TList.Create();
    New(pe);
    pe^:= Element.Create(key, item);
    table[tableIndex].add(pe);
  end;
function HashTable.Get(key : string) : Pointer;
  var
    tableIndex, i : integer;
    pe : PElement;
  begin
    result := nil;
    tableIndex := getHash(key);
    if table[tableIndex] <> nil Then
      for i := 0 to (table[tableIndex].Count - 1) do
        begin
          pe := PElement(table[tableIndex].Items[i]);
          if ((pe <> nil) and (pe^.getKey() = key)) Then
                begin
                  result := PElement(pe^.getElement());
                  break;
            end;
        end;
  end;
function HashTable.Sort(Compare : TListSortCompare) : PTList;
  var
    pl : PTList;
  begin
    new(pl);
    pl^ := TList.Create();
    merge(pl);
    pl^.Sort(Compare);
    result := pl;
  end;
{Private functions}
function HashTable.getHash(key : string): integer;
  var
    hash, i : integer;
  begin
    hash := Length(key) mod 256;
    for i := 0 to (Length(key) -1) do
      begin
        hash := integer(key[i]) mod 256;
        hash := x[hash];
      end;
    result := hash mod (tableSize + 1);
  end;
procedure HashTable.merge(pl : PTList);
  var
    i, j : integer;
  begin
    for i := 0 to tableSize do
      begin
        if table[i] <> nil Then
          begin
            for j := 0 to (table[i].Count - 1) do
              begin
                if table[i].Items[j] <> nil Then pl^.add(PUser(PElement(table[i].Items[j])^.getElement()));
              end;
          end;
      end;
  end;
end.
Jakob Justsen
 
	
	
	
	
	
	
	Hvad synes du om denne artikel? Giv din mening til kende ved at stemme via pilene til venstre og/eller lægge en kommentar herunder.
	
	Del også gerne artiklen med dine Facebook venner:  
	
	
	
	
Kommentarer (4)
		
		
			Ganske god artikel. Samme funktionalitet kunne være opnået med en Tlist af Tlists og brug af Is og As opratorer.
Dette er dog langt hutigere.
		
	
		
		
			Meget god artikel! Du beskriver på god og nem måde hvad hash-tabeller i det hele taget er, og giver en forståelig implementation til sidst. Sådan skal det være...!
		
	
		
		
			Meget god og forklarende artikel om et yderst brugbart emne! thumbs up!
		
	
		
		
			Det er sært, jeg troede hash var noget man røg 
 
		
		
	Du skal være 
logget ind for at skrive en kommentar.