Listen in Delphi

Gespeichert von Lemmy am Mo., 27.08.2018 - 20:31

Dieses Tutorial beschäftigt sich mit Listen in Delphi. Schon jeder, der ein Programm in Delphi geschrieben hat, hat auch mit Listen gearbeitet, aber nicht jeder hat es bemerkt. Es gibt einige Listenklassen in Delphi, hier eine kleine Übersicht:

  • TList Eine Zeigerliste
  • TThreadList Eine thread-sichere Zeigerliste
  • TBucketList Eine Zeigerliste, als Hash-Liste organisiert
  • TObjectBucketList Eine Liste von Objektinstanzen, als Hash-Liste organisiert
  • TObjectList Eine speicherverwaltete Liste von Objektinstanzen
  • TComponentList Eine speicherverwaltete Liste von Komponenten (d.h. Instanzen von Klassen, die sich von TComponent ableiten)
  • TClassList Eine Liste von Klassentypen
  • TInterfaceList Eine Liste mit Schnittstellenzeigern
  • TQueue Eine FIFO-Liste von Zeigern
  • TStack Eine LIFO-Liste von Zeigern
  • TObjectQueue Eine FIFO-Liste von Objekten
  • TObjectStack Eine LIFO-Liste von Objekten
  • TCollection Basisklasse für viele spezielle Klassen mit typisierten Elementen
  • TStringList Eine Stringliste
  • THashedStringList Eine Stringliste der Form Name=Wert, als Hash-Liste organisiert

Die bekannteste von diese Listen dürfte wohl die TStringList sein. Die Eigenschaft „Text“ eines Memofeldes ist nämlich vom Typ TStringList. Daneben gibt es aber noch eine Menge weiterer Insteressanter Listen. Hier in diesem Tutorial werde ich auf folgende Listenklassen eingehen:

  • TStringList / THashedStringList
  • TList
  • TObjectList / TComponentList

Alle weiteren Klassen erklären sich dann selbst bzw. lassen sich mit Unterstützung der Onlinehilfe von Delphi leicht erforschen. Was sind Listen? Eine wohl berechtigte Frage, wenn man noch nie bewusst damit gearbeitet hat. Mit Listen ist es möglich eine große Anzahl von Werten zu speichern. Sicher kann man das auch in Arrays machen – Listen sind dafür aber oft viel besser geeignet, da diese gewisse Funktionen zum hinzufügen, löschen und bearbeiten von Werten gleich mitbringen. TStringList und THashedStringList Die erste Listenklasse die ich vorstelle ist TStringList. Diese wird in einigen Komponenten der VCL verwendet (z.B TMemo, TvalueListEditor,...). Die Aufgabe ist es Texte (Strings) zu verwalten. Hier erst einmal die wichtigsten Eigenschaften und Methoden, die sich von selbst erklären: Eigenschaften:

  • Text: Mit Text kann man auf den kompletten Text zugreifen der in der TStringLIst gespeichert ist
  • Values / ValueFromIndex / Names: Wenn der Text in der Art = angeordnet ist (wie in einer Ini-Datei), kann über Values[Name] auf den Wert eines Schlüssels zugegriffen werden, mit ValueFromIndex[Index] über den Index und mittels Names[Index] auf den Schlüssel selbst.
  • Sorted: Legt fest, ob der Text (bei Schlüssel-Werte-Paaren) automatisch sortiert werden soll
  • Objects: Mit den Methoden InsertObjects und AddObjects können auch Objekte der Liste hinzugefügt werden. Zugriff erfolgt über die Eigenschaft Objects[Index].
  • Strings: Mit Strings[Index] kann auf die einzelnen Zeilen des Textes zugegriffen werden.

Methoden:

  • create: Mit Create wird ein Objekt einer TStringList erzeugt. Bei Verwendung von VCL-Klassen passiert das automatisch.
  • Free: Freigabe des Objektes
  • LoadFromFile / SaveToFile: Mit diesen Methoden wird eine Datei geladen bzw. gespeichert. Als Parameter muss der Dateiname angegeben werden.
  • LoadFromStream / SaveToStream: Mit diesen Methoden wird der Text des Objektes in einem Stream gespeichert bzw. von einem Stream geladen. Das kann z.B notwendig werden, wenn man eine ASCII-Datei untersuchen will, die sehr groß ist. Die Dateien werden nicht komplett in den Speicher geladen, sondern sie werden in Teilen von der Festplatte gelesen.

THasedStringList ist von TStringList abgeleitet und kann verwendet werden, wenn eine große Anzahl von Stringelementen in der Liste verwaltet werden sollen. Intern werden die Strings in einer Hash-Liste abgespeichert. Eine Hash-Liste verwendet einen speziellen Algorithmus für die Speicherung und den Zugriff. Dadurch wird die Leistung wesentlich optimiert und sie wird daher dann verwendet, wenn viele Daten verarbeitet werden sollen. Verwendung von TStringList Wie bei allen Objekten beginnt die Arbeit bei Create:

procedure TForm1.Button1Click(Sender: TObject);
var TempList: TStrings; {Liste deklarieren }
begin
  TempList := TStringList.Create; {Listenobjekt erzeugen}
  try {Stringliste verwenden }
    TempList.Text:=’Hallo Welt’;
  finally
    TempList.Free; {Listenobjekt freigeben }
  end;
end;

Im Großen und Ganzen also nichts besonderes. Wenn die StringList innerhalb eines Formulares in verschiedenen Methoden verwendet werden, kann die Instanziierung der StringList in OnCreate Ereignis des Forms durchgeführt werden. Aber nicht vergessen die StringList im OnClose Ereignis des Formulars wieder freizugeben! Um Strings der Liste hinzuzufügen einfach eine der entsprechenden Eigenschaften oder Methoden verwenden:

procedure Test; var TempList:TStringList;
begin
  TempList:=TStringList.Create;
  Try
    TempList.Text:=’Hallo Welt’#13’Das ist in der 2. Zeile’;
    TempList.Add(’Das wäre dann die dritte Zeile!’);

    TempList.Insert(1,’Das ist die neue 2. Zeile.’);
  Finally
    TempList.free;
  End;
End;

Bei der Zuweisung an die Eigenschaft Text wird alles, was bisher in der StringList gespeichert war, gelöscht. TStringList wird wohl am meisten verwendet, um mit Schlüssel-Werte-Paaren zu arbeiten:

procedure Test;
var TempList:TStrginList;
begin
  TempList:=TStringList.Create;
  Try
    TempList.Add(‘Wert1=Hallo’);
    TempList.Add(‘Wert2=Welt’);
    Messagedlg(TempList.Values[‘Wert1’]+’ ‘+TempList.Values[‘Wert2’],mtInformation,[mbOK],0);
  Finally
    TempList.free;
  End;
End;

Wenn man viel mit Ini-Files arbeitet, kann die TStringList auch einiges an Arbeit abnehmen.

procedure TestIni(IniFile:TIniFile);
var IniList:TStringList;
  I:Integer;
begin
  IniList:=TStringList.Create;
  Try
    IniFile.ReadSection(‘Werte’,IniList);
    If IniList.Values[‘Wert1’]=’Hallo Welt’ then
      IniList.Values[‘Wert2’]=’Das ist ein Test’;
    For i:=0 to IniList.Count-1 do
    begin
      IniFile.Write(‘Werte’,IniList.Names[i],IniList.ValueFromIndex[i]);
    end;
  Finally
    IniList.Free;
  End;
End;

Über die Methode ReadSection kann eine Sektion mit Werten aus der Ini-Datei ausgelesen werden und anschließend bearbeitet werden. Über eine For-To-Do Schleife werden die Werte ganz einfach wieder zurück geschrieben. In TStringList können auch Objekte verwaltet werden und damit so ziemlich alles was man mit Delphi bearbeiten kann. In der Klasse TObjectList können ebenfalls Objekte abgelegt werden, allerdings kann man dort nur über den Index auf die Objekte zugreifen, in der TStringList auch über einen Namen.

procedure TForm1.Button1Click(Sender: TObject);
var tsList:TStringList;
lbl:TLabel;
begin

  tsList:=TStringList.Create;
  try
    lbl:=TLabel.Create(self);
    lbl.Parent:=self;
    lbl.SetBounds(5,5,60,22);
    lbl.Caption:='Label1';
    tsList.AddObject('Label1',lbl);
    lbl:=TLabel.Create(self);
    lbl.Parent:=self;
    lbl.SetBounds(5,35,60,22);
    lbl.Caption:='Label2';
    tsList.AddObject('Label2',lbl);
    lbl:=TLabel(tsList.Objects[tsList.IndexOf('Label1')]);
    lbl.Caption:='Hallo Welt!';
  finally
    tsList.Free;
  end;
end;

Hier ein Beispiel, in dem zwei Labels verwaltet werden. Über den Namen kann ganz einfach darauf zugegriffen werden. TList TList ist einen spezielle Listenklassen. Diese kann nur Pointer verwalten. Vielleicht kenn der eine oder der andere von der Schule die verketteten Listen. Mit diesen Listen konnte eine beliebige Anzahl von beliebigen Typen gespeichert werden. In TurboPascal die einzige Möglichkeit um eine unbestimmte Anzahl von Elementen zu speichern. Allerdings muss die verkettete Liste um auf ein bestimmtes Element zugreifen zu können, sequentiell durchlaufen werden. TList bietet den verketteten Listen gegenüber wesentliche Vorteile. Neben Methoden um neue Elemente aufzunehmen und zu löschen.

type

  TTestRecord = Record
    Wert1:String;
    Wert2:Double;
    Wert3:Currency;
  end;

  ptTestRecord:^tTestRecord;

procedure TForm1.Button2Click(Sender: TObject);
var pList:TList;
  pTest:ptTestRecord;
  i:Integer;
begin
  pList:=TList.Create;
  try
    new(pTEst);
    pList.Add(pTest); //tu irgendwas
  finally
    for I:=0 to pList.Count-1 do
    begin
      pTest:=pList.Items[i];
      dispose(pTest);
    end;
    pList.Free;
  end;
end;


Über eine For-To-Do Schleife kann über den Index einfach auf alle Listenelemente zugegriffen werden. Über die entsprechenden Methoden Insert, Delete und Add können weitere Elemente Hinzugefügt und gelöscht werden. Nach der Arbeit nicht vergessen wieder aufzuräumen. Die Liste gibt die Zeiger bei der eigenen Freigabe nicht frei – also immer die Pointer über Dispose freigeben! TList bietet also eine wesentlich einfachere Verwaltung von Pointermengen. TObjectList / TComponentList Mit diesen Klassen ist es möglich Objekte, die von TObject abgeleitet sind bzw. Komponenten die von TComponent abgeleitet sind, in einer Liste zu speichern. Der Aufbau und die Verwendung gleicht dabei den anderen Listen, speziell der TList. Der „Vorteil“ TList gegenüber ist, dass bei entsprechenden Verwendungen mit TObjectList und TComponentList kein Typecast notwendig wird. Achtung: Bei Verwendung dieser Klassen muss die Unit „Contnrs“ eingebunden werden! Schon beim Erzeugen der Objekte einer der Klassen wird deutlich, dass hier wieder etwas mehr Komfort vorhanden ist:

procedure TForm1.Button2Click(Sender: TObject);
var Objects:TObjectList;
begin
  Objects:=TObjectList.Create(true);
  try
    .....
  finally
    Objects.Free;
  end;
end;

Der Konstruktor erlaubt die Übergabe eines Parameters: constructor Create(AOwnsObjects: Boolean); overload;

Dieser bewirkt (bei true), dass die Objekte, die der Liste hinzugefügt werden, bei der Freigabe der Liste ebenfalls mit freigegeben werden! Mit Remove(Index) kann ein bestimmtes Objekt aus der Liste entfernt werden und, wenn AownsObjects=true, wird es auch gleich freigegeben. Mit Extract(Index) wird das entsprechende Objekt zwar aus der Liste entfernt, doch der Speicher nicht freigegeben. Die Funktion IndexOf funktioniert bei diesen Klassen natürlich auch. Es muss nur anstelle eines Strings ein Objekt oder eine Komponente übergeben werden. Die Listenklassen in Delphi bieten viele Möglichkeiten beliebige Daten innerhalb des Programmes im Speicher zu verwalten. Alle Anwendungsmöglichkeiten oder Eigenschaften und Funktionen können hier nicht beschrieben werden. Bitte dazu die Hilfe von Delphi zu Rate ziehen.

Wie immer: Wer Fehler findet bitte eine kurze Email an lemmy @delphi-tutorials.de Wolfgang Lemmermeyer aka Lemmy