AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Zurück Delphi-PRAXiS Code-Bibliothek Neuen Beitrag zur Code-Library hinzufügen Delphi "Array" mit Strings als Indizes (Zuordnungstabelle)

"Array" mit Strings als Indizes (Zuordnungstabelle)

Ein Thema von blackdrake · begonnen am 15. Mär 2009 · letzter Beitrag vom 27. Mär 2009
Antwort Antwort
Seite 1 von 2  1 2   
blackdrake

Registriert seit: 22. Aug 2003
Ort: Bammental
618 Beiträge
 
Delphi 10.3 Rio
 
#1

"Array" mit Strings als Indizes (Zuordnungstabelle

  Alt 15. Mär 2009, 21:45
Wer viel mit PHP zu tun gehabt hat, wird bei Delphi sicherlich dynamische Arrays vermissen, die Strings als Indizes haben. Da ich gerade an einem Projekt arbeite, bei dem der Benutzer einem Objekt einen konkreten Name-Identifier geben soll, habe ich mal eine Klasse erstellt, die einen String ein TObject zuweist. Dieses TObject kann man dann mit konkreten Instanzen einer TObject-Ableitung belegen (z.B. einer TImage-Instanz).

Delphi-Quellcode:
interface

uses
  Contnrs;

type
  TEasyObjectManager = class(TObject)
  published
    constructor Create;
    destructor Destroy; override;
  public
    function GetValue(Name: string): TObject;
    procedure SetValue(Name: string; Value: TObject);
    function ValueExists(Name: string): boolean;
    procedure DeleteValue(Name: string);
    function Count: integer;
    function GetValueById(Index: integer): TObject;
  private
    List: TObjectList;
  end;

implementation

type
  TNameObjectPair = class(TObject)
    Name: string;
    Value: TObject;
  end;

(*** TEasyObjectManager ***)

constructor TEasyObjectManager.Create;
begin
  inherited;
  // OwnsObject ist false. Das heißt, die Objekte, die wir verwalten,
  // werden nicht freigegeben.
  List := TObjectList.Create(false);
end;

destructor TEasyObjectManager.Destroy;
begin
  if Assigned(List) then List.Free;
  inherited;
end;

function TEasyObjectManager.GetValue(Name: string): TObject;
var
  i: integer;
begin
  result := nil;
  for i := 0 to List.Count-1 do
  begin
    if TNameObjectPair(List.Items[i]).Name = Name then
    begin
      result := TNameObjectPair(List.Items[i]).Value;
      break;
    end;
  end;
end;

procedure TEasyObjectManager.SetValue(Name: string; Value: TObject);
var
  i: integer;
  n: TNameObjectPair;
  found: boolean;
begin
  found := False;
  for i := 0 to List.Count-1 do
  begin
    if TNameObjectPair(List.Items[i]).Name = Name then
    begin
      TNameObjectPair(List.Items[i]).Value := Value;
      found := true;
      break;
    end;
  end;
  if not found then
  begin
    UniqueString(Name);
    n := TNameObjectPair.Create;
    n.Name := Name;
    n.Value := Value;
    List.Add(n);
  end;
end;

function TEasyObjectManager.ValueExists(Name: string): boolean;
var
  i: integer;
begin
  result := false;
  for i := 0 to List.Count-1 do
  begin
    if TNameObjectPair(List.Items[i]).Name = Name then
    begin
      result := true;
      break;
    end;
  end;
end;

procedure TEasyObjectManager.DeleteValue(Name: string);
var
  i: integer;
begin
  for i := 0 to List.Count-1 do
  begin
    if TNameObjectPair(List.Items[i]).Name = Name then
    begin
      List.Delete(i);
      break;
    end;
  end;
end;

function TEasyObjectManager.Count: integer;
begin
  result := List.Count;
end;

function TEasyObjectManager.GetValueById(Index: integer): TObject;
begin
  result := List.Items[Index].Value;
end;
Ein Anwendungsbeispiel:

Delphi-Quellcode:
var
  x: TEasyObjectManager;
begin
  x := TEasyObjectManager.Create;
  try
    x.SetValue('foo', Form1); // foo wird angelegt
    ShowMessage(TForm(x.GetValue('foo')).Caption);
    x.SetValue('foo', Button1); // foo wird überschrieben
    ShowMessage(TButton(x.GetValue('foo')).Caption);
  finally
    x.Free;
  end;
end;
Anmerkung: Die Indizes sind natürlich case sensitive, also x.GetValue('A') ist etwas anderes als x.GetValue('a').

Count() und GetValueById() können verwendet werden, um alle Elemente der Zuordnungstabelle durchzugehen. Dies wäre dann mit einer For-Schleife möglich (In PHP würde man für diesen Zweck eine Foreach-Schleife verwenden).

Delphi-Quellcode:
var
  i: integer;
begin
  for i := 0 to List.Count - 1 do
  begin

  end;
end;
Diese Klasse müsste jedoch modifiert werden, wenn ihr Strings, Integer, Booleans oder Floats anstelle von Objekten verwalten wollt. Das wäre aber in sofern ungünstig, da dann viel rudundanter Code vorliegen würde. Ich habe mir jetzt von Java noch eine Interessante Funktionalität abgeguckt. Dort ist zwar der String ein abstrakter Datentyp, also eine Klasse, jedoch ist auch ein Integer bei Java ein einfacher Datentyp (int). Der Vorteil in Java: Weise ich ein int einem Object zu, wird dieser automatisch in eine Integer-Containerklasse gewandelt (toll!). In Delphi könnt ihr also folgende Helferklassen für meine Zuordnungsklasse verwenden:

Delphi-Quellcode:
type
  TString = class(TObject)
    Content: string;
  end;

  TInteger = class(TObject)
    Content: integer;
  end;

  TFloat = class(TObject)
    Content: float;
  end;

  TBoolean = class(TObject)
    Content: boolean;
  end;
Die Verwendung muss dann so aussehen:

MeinInteger := TInteger(x.GetValue('MeineZahl')).Content; Wer natürlich ausschließlich mit Integers o.ä. arbeiten will, kann natürlich meinen Code editieren und alle TObject umwandeln. Verwaltet ihr aber Strings, müssen die Values mit UniqueString() speichertechnisch einzigartig gemacht werden:

Delphi-Quellcode:
procedure TEasyStringManager.SetValue(Name: string; Value: string);
var
  i: integer;
  n: TNameStringPair;
  found: boolean;
begin
  found := False;
  UniqueString(Value); // <-- Bei einer String-String Zuordnungstabelle ist diese Zeile zusätzlich nötig!
  for i := 0 to List.Count-1 do
  ...
Für Verbesserungsvorschläge bin ich natürlich immer offen!

Gruß
blackdrake
Daniel Marschall
  Mit Zitat antworten Zitat
Dax
(Gast)

n/a Beiträge
 
#2

Re: "Array" mit Strings als Indizes (Zuordnungstab

  Alt 15. Mär 2009, 21:54
Das gibt es bereits, mit viel besserer Laufzeit: Hash-TabellenHash-Tabellen
  Mit Zitat antworten Zitat
Cyf

Registriert seit: 30. Mai 2008
407 Beiträge
 
Lazarus
 
#3

Re: "Array" mit Strings als Indizes (Zuordnungstab

  Alt 15. Mär 2009, 22:42
TStringList Oder besser:

Zitat:
THashedStringList verwaltet eine Stringliste mit einer internen Hash-Tabelle.

Unit
IniFiles

Syntax


[Delphi] type THashedStringList = class (Classes.TStringList);


Beschreibung
Ein THashedStringList-Objekt ist eine Stringliste, die intern eine Hash-Tabelle verwendet, um die Suche nach Strings zu beschleunigen. Es wird intern von TMemIniFile verwendet, um die Strings in einer INI-Datei zu verwalten. Das Objekt kann jedoch wie jede andere Stringliste genutzt werden. Insbesondere bei Listen mit einer großen Anzahl von Strings kann die Leistung durch die Verwendung der Klasse THashedStringList anstelle von TStringList optimiert werden.
Man kann einen Barbier definieren als einen, der alle diejenigen rasiert, und nur diejenigen, die sich nicht selbst rasieren.
Rasiert sich der Barbier?
  Mit Zitat antworten Zitat
blackdrake

Registriert seit: 22. Aug 2003
Ort: Bammental
618 Beiträge
 
Delphi 10.3 Rio
 
#4

Re: "Array" mit Strings als Indizes (Zuordnungstab

  Alt 15. Mär 2009, 22:44
Hallo. Das mit den Hash-Tabellen schaue ich mir mal an und schaue, ob diese Klasse alles kann, was meine auch kann.

Aber TStringList kann doch die Funktionalität nicht wirklich ersetzen, oder? Eine TStringList ordnet Integer -> String/Object zu. Ich will aber String -> Object.
Daniel Marschall
  Mit Zitat antworten Zitat
Cyf

Registriert seit: 30. Mai 2008
407 Beiträge
 
Lazarus
 
#5

Re: "Array" mit Strings als Indizes (Zuordnungstab

  Alt 15. Mär 2009, 22:50
Eine TStringList verwaltet Strings , optional kann man jedem ein Object hinzufügen.
Die Hashed-Variante ist in der Regel zu bevorzugen, jedoch nicht bei älteren Delphis verfügbar.
Man kann einen Barbier definieren als einen, der alle diejenigen rasiert, und nur diejenigen, die sich nicht selbst rasieren.
Rasiert sich der Barbier?
  Mit Zitat antworten Zitat
Benutzerbild von jaenicke
jaenicke

Registriert seit: 10. Jun 2003
Ort: Berlin
7.914 Beiträge
 
Delphi 10.4 Sydney
 
#6

Re: "Array" mit Strings als Indizes (Zuordnungstab

  Alt 15. Mär 2009, 23:55
Zitat von blackdrake:
Aber TStringList kann doch die Funktionalität nicht wirklich ersetzen, oder? Eine TStringList ordnet Integer -> String/Object zu. Ich will aber String -> Object.
Mit einer TStringList kann man nach Belieben standardmäßig Integer --> String (via Index), Integer --> Object (via Index), String --> Object (via IndexOf), String --> String (via Names).
Bei optimierter Implementierung kann aber eine eigene Klasse trotzdem sinnvoll sein. Dies ist eventuell das Interessante bei einer eigenen Implementierung.

Die Hashvariante oder eine andere Implementierung mit Hashes ist aber meistens noch besser, das ist sicher ebenfalls richtig.
Sebastian Jänicke
Alle eigenen Projekte sind eingestellt, ebenso meine Homepage, Downloadlinks usw. im Forum bleiben aktiv!
  Mit Zitat antworten Zitat
Muetze1
(Gast)

n/a Beiträge
 
#7

Re: "Array" mit Strings als Indizes (Zuordnungstab

  Alt 16. Mär 2009, 01:36
... und kann auch mit Strings als Index arbeiten, wenn man sich auf das Format Name=Wert einlässt. Aber wie die TStrings das intern handeln, kann einem ja egal sein.

Also - mal abgesehen von der THashStringList - klappt die String-Als-Index Sache von Haus aus mit TStringList. Siehe dazu [oh]TStrings.Values[][/oh].
  Mit Zitat antworten Zitat
alzaimar
(Moderator)

Registriert seit: 6. Mai 2005
Ort: Berlin
4.956 Beiträge
 
Delphi 2007 Enterprise
 
#8

Re: "Array" mit Strings als Indizes (Zuordnungstab

  Alt 16. Mär 2009, 07:14
Hier ist ein Performancevergleich verschiedener Suchalgorithmen, u.a. TStringList, THashedStringlist und Hashmaps.
"Wenn ist das Nunstruck git und Slotermeyer? Ja! Beiherhund das Oder die Flipperwaldt gersput!"
(Monty Python "Joke Warefare")
  Mit Zitat antworten Zitat
blackdrake

Registriert seit: 22. Aug 2003
Ort: Bammental
618 Beiträge
 
Delphi 10.3 Rio
 
#9

Re: "Array" mit Strings als Indizes (Zuordnungstab

  Alt 16. Mär 2009, 08:02
Hallo.

Danke für eure Antworten.

Ich habe jetzt folgende Variante für TStringList und THashedStringList geschrieben. Dadurch wird der Code auch gleich viel schlanker.

Delphi-Quellcode:
uses
  IniFiles;

type
  TEasyObjectManager = class(TObject)
  published
    constructor Create(CaseSensitive: boolean);
    destructor Destroy; override;
  public
    function GetValue(Name: string): TObject;
    procedure SetValue(Name: string; Value: TObject);
    function ValueExists(Name: string): boolean;
    procedure DeleteValue(Name: string);
    function Count: integer;
    function GetValueById(Index: integer): TObject;
    function GetNameById(Index: integer): string;
  private
    List: THashedStringList; // Alternativ: TStringList
  end;

(*** TEasyObjectManager ***)

constructor TEasyObjectManager.Create(CaseSensitive: boolean);
begin
  inherited Create;
  List := THashedStringList.Create;
  // Ich bin der Meinung, man sollte das vorher definieren und nicht mittendrin ändern
  List.CaseSensitive := CaseSensitive;
end;

destructor TEasyObjectManager.Destroy;
begin
  if Assigned(List) then List.Free;
  inherited;
end;

function TEasyObjectManager.GetValue(Name: string): TObject;
var
  i: integer;
begin
  i := List.IndexOf(Name);
  if i <> -1 then
  begin
    result := List.Objects[i];
  end
  else
  begin
    result := nil;
  end;
end;

procedure TEasyObjectManager.SetValue(Name: string; Value: TObject);
var
  i: integer;
begin
  i := List.IndexOf(Name);
  if i <> -1 then
  begin
    List.Objects[i] := Value;
  end
  else
  begin
    UniqueString(Name); // Notwendig?
    List.AddObject(Name, Value);
  end;
end;

function TEasyObjectManager.ValueExists(Name: string): boolean;
begin
  result := List.IndexOf(Name) <> -1;
end;

procedure TEasyObjectManager.DeleteValue(Name: string);
var
  i: integer;
begin
  i := List.IndexOf(Name);
  if i <> -1 then
  begin
    List.Delete(i);
  end;
end;

function TEasyObjectManager.Count: integer;
begin
  result := List.Count;
end;

function TEasyObjectManager.GetValueById(Index: integer): TObject;
begin
  result := List.Objects[Index];
end;

function TEasyObjectManager.GetNameById(Index: integer): string;
begin
  result := List.Strings[Index];
end;
Gruß
blackdrake
Daniel Marschall
  Mit Zitat antworten Zitat
Cyf

Registriert seit: 30. Mai 2008
407 Beiträge
 
Lazarus
 
#10

Re: "Array" mit Strings als Indizes (Zuordnungstab

  Alt 16. Mär 2009, 17:57
Ok, das ist jetzt ein Wrapper für eine Klasse mit ein paar extra Methoden, um nicht jedesmal den Index mit den Funktionen dafür zu ermitteln, sondern das direkt da rein zu bauen? Wäre es nicht sinnvoller, wenn man die Klasse einfach ableitet und wenn man sowas braucht, das schnell hinzuzufügen, auch könnte das mit dem case sensitive sonst schwer werden?
Nebenbei sind auch die Abfragen, ob Einträge existieren unschön, entweder sie sollten dann angelegt werden, oder bei Abfragen sollte eine Exception geworfen werden, um zu signaliesieren, dass es den Eintrag nicht gibt, weil man nil ja auch zuweißen könnte.

// Ich bin der Meinung, man sollte das vorher definieren und nicht mittendrin ändern Ich nicht, sowas sollte in eine Property.
Man kann einen Barbier definieren als einen, der alle diejenigen rasiert, und nur diejenigen, die sich nicht selbst rasieren.
Rasiert sich der Barbier?
  Mit Zitat antworten Zitat
Antwort Antwort
Seite 1 von 2  1 2   

Themen-Optionen Thema durchsuchen
Thema durchsuchen:

Erweiterte Suche
Ansicht

Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +2. Es ist jetzt 22:40 Uhr.
Powered by vBulletin® Copyright ©2000 - 2021, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2021 by Daniel R. Wolf