Ausgehend von dieser Diskussion: >>Performanceanalyse der neuen Datenstruktur "Map"<< habe ich mir Gedanken gemacht, wie man die Folgen eines ReDim minimieren kann.
Arrays verbrauchen in der Regel exakt so viel Speicher, wie Elemente in diesem enthalten sind.
Da diese Elemente immer direkt hintereinander stehen, führt dies zu Problemen wenn man Elemente hinzufügen möchte.
Es muss erst ein größerer Speicher reserviert werden, welcher Platz für das neue Array bietet und dann müssen alle Inhalte vom alten in das neue Array kopiert werden.
Diese Aufgabe übernimmt ReDim. Je größer so ein Array wird umso länger dauert dann jedes ReDim.
Ein klassisches Array ist also denkbar ungeeignet wenn man Elemente dynamisch hinzufügen möchte.
Zwar gibt es alternative Datenstrukturen wie ArrayLists. Diese haben zudem den Vorteil ganz fix neue Elemente innerhalb des Arrays einzufügen. Aber der Direktzugriff auf zufällige Elemente ist bei diesen deutlich langsamer.
Eine Idee die Folgen von andauerndem ReDim zu minimieren ist folgender:
Wenn ein Element hinzukommt dann wird die Größe des Arrays nicht um 1 erhöht sondern gleich verdoppelt wenn kein Platz mehr im Array vorhanden ist. Damit hat man dann noch Luft für weitere kommende Elemente.
Auf diesem Weg spart man sich einige ReDim ein. Analog verhält sich das Ganze beim Entfernen von Elementen.
Folgendes ignorieren - Funktionen wurden zu einer ausgewachsenen UDF ausgebaut: >>DynArray-UDF<<
Hierzu habe ich mal eine UDF erstellt (liegt im Anhang) welche folgende Funktionen beinhaltet:
_DynArrayPush - neuen Wert ans Ende anhängen
_DynArrayPop - letzten Wert zurückgeben und aus dem Array entfernen
_DynArrayInsert - einen oder mehrere Werte innerhalb des Arrays einfügen
_DynArrayDelete - einen oder mehrere Werte innerhalb des Arrays entfernen
_DynArraySearch - Werte in einem unsortierten Array finden
_DynArrayBinarySearch - Werte in einem sortierten Array finden
_DynArraySort - Array sortieren
_DynArrayMap - eine beliebige Funktion auf alle Elemente des Arrays anwenden
Ein Beispiel zur Verwendung wäre folgendes:
Beispiel zur UDF "DynArray"
#include "DynArray.au3"; Neues ArrayGlobal $a_Array[1] = [0]; Dem Array dynamisch Elemente hinzufügenFor $x = 1 To 9 _DynArrayPush($a_Array, "Test " & $x)Next_ArrayDisplay($a_Array, "Das Array"); Einzelne Elemente innerhalb des Arrays einfügen:_DynArrayInsert($a_Array, "Insert 1", 4)_DynArrayInsert($a_Array, "Insert 2", 5); Mehrere Elemente auf einmal einfügen:Local $a_Temp[] = ["Insert 3", "Insert 4", "Insert 5", "Insert 6"]_DynArrayInsert($a_Array, $a_Temp, 6)_ArrayDisplay($a_Array, "Das Array nach _DynArrayInsert"); Element in einem unsortierten Array suchen:MsgBox(0, "_DynArraySearch", 'Wert "Test 5" am Index ' & _DynArraySearch($a_Array, "Test 5") & ' gefunden.'); Array sortieren:_DynArraySort($a_Array)_ArrayDisplay($a_Array, "Das Array nach _DynArraySort"); Element in einem sortierten Array suchen:MsgBox(0, "_DynArrayBinarySearch", 'Wert "Test 5" am Index ' & _DynArrayBinarySearch($a_Array, "Test 5") & ' gefunden.'); Das letzte Element abrufen und aus dem Array entfernen:MsgBox(0,"_DynArrayPop", 'Das letzte Element: "' & _DynArrayPop($a_Array) & '"'); Elemente löschen:_DynArrayDelete($a_Array, 4, 6)_ArrayDisplay($a_Array, "Das Array nach _DynArrayDelete"); Beispiel für ArrayMap$a_Upper = _DynArrayMap($a_Array, StringUpper)_ArrayDisplay($a_Upper, "Das Array nach _DynArrayMap")
[/autoit]Fassen wir mal die Vor-und Nachteile zusammen:Vorteile:
- Deutlich gesteigerte Performance beim Hinzufügen von Elementen.
Nachteile:
- Doppelter Speicherverbrauch im Worst-Case
- Unstetiger Zeitbedarf für jedes Hinzufügen
- Zwang zur Notation der Elementanzahl im ersten Element
Was bringt nun aber _DynArrayPush() im Vergleich zu _ArrayAdd() im Endeffekt wirklich?
Hierzu kann man bei sich einfach mal folgendes Skript ausführen welches den Zeitbedarf beim dynamischen Hinzufügen von 1000 Elementen vergleicht:
Performancevergleichsskript _DynArrayPush() -- _ArrayAdd()
#include "DynArray.au3"Global $N = 1e3$iT = TimerInit()Global $a_Array[1] = [0]For $i = 1 To $N _DynArrayPush($a_Array, "Test " & $i)Next$iT = TimerDiff($iT)ConsoleWrite(StringFormat("% 15s: \t%7.1f ms\n", "_DynArrayPush()", $iT))_ArrayDisplay($a_Array)$iT = TimerInit()Global $a_Array[] = []For $i = 1 To $N _ArrayAdd($a_Array, "Test " & $i)Next$iT = TimerDiff($iT)ConsoleWrite(StringFormat("% 15s: \t%7.1f ms\n", "_ArrayAdd()", $iT))_ArrayDisplay($a_Array)
[/autoit]Meine Ergebnisse hierbei waren folgende:
Dieser Zeitvorteil steigt exponentiell mit der Elementanzahl