Autor |
Beitrag |
moloch
      
Beiträge: 451
Win 2000
D5 Prof
|
Verfasst: Fr 09.12.05 14:04
Hallo,
bisher habe ich es so gemacht das ich meine daten in ein dynamisches Array geladen habe.
wenn ich jetzt eine Information zu einer zeile haben wollte bin ich das array mit einer schleife durchgegangen und dann die nötigen infos letztendlich gefunden.
bei einer datenmänge von ca 50000 datensätzen wird das jedoch immer langsamer. bei java gibt es das objekt hashmap soweit ich weiss und das soll dieses problem besser lösen können.
die frage ist einfach ob es in delphi auch eine schnellere möglichkeit gibt auf die jeweiligen daten zuzugreifen als mit der bisherigen methode von mir.
mfg
moloch
|
|
chrisw
      
Beiträge: 439
Erhaltene Danke: 3
W2K
D7
|
Verfasst: Fr 09.12.05 14:07
Bei 50000 Datensätzen, denke ich, sollte man zumindest über eine Datenbanklösung nachdenken !
_________________ Man sollte keine Dummheit zweimal begehen, die Auswahl ist schließlich groß genug.
|
|
Gausi
      
Beiträge: 8549
Erhaltene Danke: 478
Windows 7, Windows 10
D7 PE, Delphi XE3 Prof, Delphi 10.3 CE
|
Verfasst: Fr 09.12.05 14:12
Schon mal daran gedacht, das Array zu sortieren, und dann eine Binärsuche drauf anzusetzen?
_________________ We are, we were and will not be.
|
|
Lossy eX
      
Beiträge: 1048
Erhaltene Danke: 4
|
Verfasst: Fr 09.12.05 14:15
chrisw: Ist ne DB dafür nicht ein bisschen übertrieben.
Also ich weiß nicht was genau Java da macht aber ein paar Text Beispiele wären nicht schlecht. Ich habe auf meiner Webseite aber auch eine HashKlasse bereit gestellt. Mit dieser kannst du zu einem eindeutigen Text einen Pointer ablegen.
_________________ Nur die Menschheit ist arrogant genug, um zu glauben sie sei die einzige intelligente Lebensform im All. Wo nicht mal das nachhaltig bewiesen wurde.
|
|
moloch 
      
Beiträge: 451
Win 2000
D5 Prof
|
Verfasst: Fr 09.12.05 15:44
hallo also noch mal zum besseren verständnis meiner vorgehensweise.
DB ist z.B. MSSQL
Jetzt lade ich mir per sql befehl daten in mein dynamisches array.
so und jetzt geht es darum dass ich wärend das programm läuft aus verschiedenen gründen auf bestimmte datensätze zugreifen möchte. z. b. um die daten anzuzeigen in einer oberfläche.
der bisherige weg war dann der:
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9:
| function Get_Daten(ID : Integer) : String; begin for i:= (array) to (array) do begin if (array[i].id <> id) then continue; result := array[i].name; break; end; end; |
so und ich suche jetzt eine schnellere lösung.
Moderiert von raziel: Delphi-Tags hinzugefügt.
|
|
moloch 
      
Beiträge: 451
Win 2000
D5 Prof
|
Verfasst: Fr 09.12.05 17:25
kannst du mir ein beispiel geben wie ich mit deiner hashmap lösung umgehen könnte
|
|
Lossy eX
      
Beiträge: 1048
Erhaltene Danke: 4
|
Verfasst: Mo 12.12.05 11:14
Sorry das es ein bisschen gedauert hat. War übers WE nicht im Forum.
Ich weiß jetzt nicht was du vor hast darin abzulegen. Also ob es sich nur im einen einzelnen Text der eine Record handelt. Bei beiden musst du aber beachten, dass du in dem Hash ausschließlich nur den Pointer von etwas ablegen kannst. Du musst entsprechende mit einem Pointertypen arbeiten und diesen erstellen und am Ende wieder frei geben.
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71:
| procedure TForm1.FormCreate(Sender: TObject); begin fHash := TIntegerHash.Create(1000); end;
procedure TForm1.FormDestroy(Sender: TObject); var Idx: Integer; Vals: TList; begin Vals := TList.Create; try fHash.GetValues(Vals);
for Idx := 0 to Vals.Count -1 do StrDispose(PChar(Vals[Idx])); finally FreeAndNil(Vals); end;
FreeAndNil(fHash); end;
procedure TForm1.Button1Click(Sender: TObject); var Text: String; begin Inc(fCount); Text := 'Text' + IntToStr(fCount);
fHash.Add(fCount, StrNew(PChar(Text))); end;
procedure TForm1.Button2Click(Sender: TObject); var Idx, Num: Integer; Names: TList; begin Memo1.Lines.BeginUpdate; try Memo1.Lines.Clear;
Names := TList.Create; try fHash.GetNames(Names);
for Idx := 0 to Names.Count -1 do begin Num := Integer(Names[Idx]);
Memo1.Lines.Add(IntToStr(Num) + #9 + PChar(fHash.Get(Num))); end; finally FreeAndNil(Names); end; finally Memo1.Lines.EndUpdate; end; end; |
Beim Erstellen des Hashs musst du eine Größe angeben. Die ist im nachhinein nicht änderbar außer man erstellt ein komplett neues Hash. Da die Werte intern mit der Größe verrechnet werden. Wenn deine IDs nicht gerade vortlaufend sind so passiert es häufig, dass jedes zweite, dritte oder vierte Feld leer bleibt. In einem Feld sind dann minilisten gespeichert. Wenn du eine Größe von 1 hast wären alle Werte in einer Liste gespeichert. Dann wäre es sogar noch langsamer als ein Array oder TList. Also musst du entsprechend viele Felder erstelen lassen um eine möglichst breite Streueung zu erhalten. Bei 50000 Datenfelder würde ich schon sagen eine größe um die 20000 wäre sinnvoll. Das sollte dann vom Speicherverbrauch auch noch halbwegs gehen. Primzahlen wird nachgesagt sich für solche Feldgrößen am Besten zu eignen.
_________________ Nur die Menschheit ist arrogant genug, um zu glauben sie sei die einzige intelligente Lebensform im All. Wo nicht mal das nachhaltig bewiesen wurde.
|
|
moloch 
      
Beiträge: 451
Win 2000
D5 Prof
|
Verfasst: Mo 12.12.05 16:53
PByte
kennt mein delphi 5 leider nicht
vielleicht wird es noch mehr unbekanntes geben aber damit fängt der compiler an zu meckern
|
|
moloch 
      
Beiträge: 451
Win 2000
D5 Prof
|
Verfasst: Mo 12.12.05 17:09
peinlich vergiss diese frage.
ok ich probier weiter...
danke erstmal für die hilfe ich sag bescheid wie ich damit voran komme
|
|
alzaimar
      
Beiträge: 2889
Erhaltene Danke: 13
W2000, XP
D6E, BDS2006A, DevExpress
|
Verfasst: Mo 12.12.05 17:35
Alternative zu den Integer-Hashtabellen sind die hier, die passen ihre Größe dynamisch an, natürlich als Primzahl.
@LossyEx: Schau doch mal in den Code hier und übernimm (von mir aus) die dynamische Größenveränderung. Deine String->Hash Funktion ist auch nicht die Beste (zumindest laut Literatur nicht). Nimm doch eine von Meinen, wenn Du willst. Deine Implementierung ist ja (logischerweise) fast identisch mit Meiner. Kein Wunder: Wir haben beide das beste Verfahren (chained hash). Ich habe noch einen Iterator, Du dafür ein paar andere Methoden... Beides zusammen wäre doch was.
Interessant sind auch Skiplisten, die bis ca. 50000 Elemente noch schneller als Hashetabellen sind (bei Strings). Code hab ich gerade nicht hier, sondern auf meinem Laptop. Bei Bedarf poste ich das aber gerne.
Einloggen, um Attachments anzusehen!
_________________ Na denn, dann. Bis dann, denn.
|
|
Lossy eX
      
Beiträge: 1048
Erhaltene Danke: 4
|
Verfasst: Di 13.12.05 10:23
Habe mir die Theorie vor ein paar Jahren aus einem Algorithmenbuch angeeignet. Allerdings habe ich nur die ersten 4 Seiten der Hashs geschafft. Alles was dahinter kam war zu hoch für mich. Habe die Klassen dann mehr oder minder aus meinen Erinnerungen geschrieben. Aber werde mir das auf jeden Fall mal anschauen.  Kann allerdings noch nicht versprechen, dass ich etwas ändern werde, denn ich weiß noch nicht wann ich dafür dann zeit habe. Viele wichtige Dinge stehen an. 
_________________ Nur die Menschheit ist arrogant genug, um zu glauben sie sei die einzige intelligente Lebensform im All. Wo nicht mal das nachhaltig bewiesen wurde.
|
|
moloch 
      
Beiträge: 451
Win 2000
D5 Prof
|
Verfasst: Di 13.12.05 11:38
hallo,
also das habe ich noch nicht ganz verstanden.
ich gehe wie folgt vor:
1.ich möchte ein vorhandenes dynamisches Array in ein Hash legen.
das würde dann so aussehen:
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12:
| i : Integer; Ptr : Pointer; begin result := TStringHash.Create(length(HilfArr));
for i := low(HilfArr) to high(HilfArr) do begin
result.Add(HilfArr[i].Name, HilfArr[i].ID, Ptr) end; end; |
das funktioniert so nicht. das liegt mit sicherheit daran das ich PTR also den Pointer nicht bestimme.wie geht das und wird es dann funktionieren oder hab ich noch was falsch gemacht?
Moderiert von Gausi: Delphi-Tags hinzugefügt.
|
|
Lossy eX
      
Beiträge: 1048
Erhaltene Danke: 4
|
Verfasst: Di 13.12.05 12:45
Was befindet sich denn in dem Array? Also ich denke mal ein Record. Poste mal Bitte wie das aussieht, dann bekommste auch ein bissel Code.
Aber im Endeffekt würde ich soweiso sagen, dass du das Array komplett entfernen solltest und das Hash gleich von Begin an füllen solltest. Das ist wesentlich einfacher als erst das Array befüllen und das dann in das Hash zu kippen.
_________________ Nur die Menschheit ist arrogant genug, um zu glauben sie sei die einzige intelligente Lebensform im All. Wo nicht mal das nachhaltig bewiesen wurde.
|
|
moloch 
      
Beiträge: 451
Win 2000
D5 Prof
|
Verfasst: Di 13.12.05 13:04
Name und ID dazu
ich will mit dem namen reingehen und die ID holen
|
|
Lossy eX
      
Beiträge: 1048
Erhaltene Danke: 4
|
Verfasst: Di 13.12.05 13:46
Also ich vertrete die Auffassung, dass eine Methode nur in sehr seltenen Fällen eine Klasse erstellen sollte und diese zurückgibt. Mit anderen Worten ich achte immer darauf, dass derjenige der irgendwelche Objekte erstellt diese auf wieder frei gibt. Bzw wenn er diese nicht freigeben kann, dass er sie auch nicht erstellt. Vermeidung von Speicherlöchern.
Zu deinem Code oberhalb. Wenn du es unbedingt so machen möchtest solltest du aber auch darauf achten, dass du Fehler abfängst und entsprechend darauf reagierst. Eine sehr gute Möglichkeit dazu wäre Try Except bzw Try Finally.
Wenn ich das richtig verstanden habe möchtest du anhand der Namen eine ID auswählen können? Einen Integer kannst du nicht direkt ablegen, da Integer und Pointer aber beide 32 Bit groß sind können die sich den Speicherplatz teilen.
Hier mal der veränderte Code.
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29:
| i : Integer; begin result := TStringHash.Create(length(HilfArr) div 3); try
for i := low(HilfArr) to high(HilfArr) do result.Add(HilfArr[i].Name, Pointer(HilfArr[i].ID))
except on e:exception do begin FreeAndNil(Result);
raise e; end; end; end; |
_________________ Nur die Menschheit ist arrogant genug, um zu glauben sie sei die einzige intelligente Lebensform im All. Wo nicht mal das nachhaltig bewiesen wurde.
|
|
moloch 
      
Beiträge: 451
Win 2000
D5 Prof
|
Verfasst: Mi 14.12.05 14:25
|
|
Lossy eX
      
Beiträge: 1048
Erhaltene Danke: 4
|
Verfasst: Mi 14.12.05 14:49
Bitte benutz die Delphi-tags. Ich denke mal die Mods haben besseres zu tun als ständig die Tags bei dir einfügen zu müssen.
Okay. Vom Prinzip her geht es auch. Aber du hast so ein paar Nachteile. Du musst beim Beenden auf jeden Fall alle Pointer mit GetValues in eine TList holen und diese dann auch wieder frei geben. Außerdem benutzt ein komplexes Record um eine einfache Zahl abzulegen. In dem Record enthalten ist dann aber auch noch ein ungenutzter Text. Sozusagen verschwendest du derzeit pro Eintrag 8 Bytes Speicher was dir aber keinen Vorteil bringt. Nach außen hin dürfte es sowieso auch keinen Unterschied machen.
Aber ist deine Anwendung kannste also auch so lassen.
_________________ Nur die Menschheit ist arrogant genug, um zu glauben sie sei die einzige intelligente Lebensform im All. Wo nicht mal das nachhaltig bewiesen wurde.
|
|
moloch 
      
Beiträge: 451
Win 2000
D5 Prof
|
Verfasst: Mi 14.12.05 16:09
aber wenn ich das programm beende wird der speicher von selbst frei gemacht.oder täuscht das nur.
|
|
moloch 
      
Beiträge: 451
Win 2000
D5 Prof
|
Verfasst: Mi 14.12.05 16:13
und wieso verschwende ich damit speicher.das hab ich nicht ganz verstanden
|
|
Lossy eX
      
Beiträge: 1048
Erhaltene Danke: 4
|
Verfasst: Mi 14.12.05 16:23
Windows räumt dann alles weg was noch da ist. Aber das ist nicht die beste Einstellung. Es handelt sich dennoch um Leichen. Wenn du diese bis zu letzt benutzt mag das nicht so schlimm sein aber solltest du sie nur kurz benutzen, dann solltest du sie auf jeden Fall wieder frei geben. Wobei ich der Meinung bin, dass man IMMER alles wieder freigeben sollte.
Das mit dem Speicher verschwenden ist einfach. Mit meiner Lösung würde ich lediglich den Pointer benutzen um die ID abzulegen. Du erstellst aber einen extra Speicherbereich in dem du die ID ablegst und benutzt den Pointer des Spiechers. Dadurch, dass du sogar das Record erstellst hast du auch noch einen Pointer auf den String bei jedem eintrag dabei. Und auch diese Pointervariable muss irgendwo Speicher belegen.
_________________ Nur die Menschheit ist arrogant genug, um zu glauben sie sei die einzige intelligente Lebensform im All. Wo nicht mal das nachhaltig bewiesen wurde.
|
|