Sonntag, 30. Juni 2013

Arrays

Hallo zusammen zu diesem Post! 
 
Heute erweitern wir unser Wissen im Themenbereich der Variablen, denn wir schauen uns an, wie wir eine Ansammlung von Variablen erstellen und verwalten können. Diese Ansammlungen von Variablen nennt man "Arrays". 
Arrays werden häufig dazu benutzt, Variablen, die zu einer Kategorie gehören, auch zusammen zu speichern. Als Beispiel nehmen wir hier einmal Obst. Wir wollen einige Obstsorten in ein Array zusammenfassen, das sich dann logischerweise "Obst" nennt. 
Um ein Array zu erstellen, müssen wir erst einmal wissen, welchen Variablentyp wir speichern wollen. Wir nehmen in diesem Fall ein String-Array, da es die Klasse Obst noch nicht gibt. Dies können wir ändern, sobald die Objektorientierung an die Reihe kommt, denn es kann hier jede Klasse als Basis-Datentyp verwendet und so zu einem Array verwendet werden. 
Um ein String-Array zu deklarieren, schreiben wir: 
 
string[] obst; 
 
Der Variablentyp wird hier noch mit zwei eckigen Klammern versehen, damit der Compiler weiß, dass es sich um ein Array handelt, sonst sieht die Deklaration gleich aus, wie bei einer normalen Variable. 
Jetzt weiß der Compiler, dass er, sobald wir das Array auch initialisieren, einige Strings erstellen soll. Der Unterschied zu Variablen besteht darin, dass bei einem Array alle strings im Arbeitsspeicher nebeneinander liegen. Bis jetzt weiß der Compiler allerdings noch nicht, wie viele strings reserviert erden sollen, sondern nur, welche Größe eine Variable hat, wobei diese Aussage bei einem String auch nicht zutrifft, da ein String beliebig groß sein kann. Würden wir hier aber ein int-Array erstellen, so wüsste der Compiler, dass pro angeforderte Array-Variable 32 bit Arbeitsspeicher belegt werden müssten.  
Um ein Array zu initialisieren, gibt es mehrere Möglichkeiten, von denen wir uns die einfachste zuerst anschauen: 
Nachdem wir das Array deklariert habe (in diesem Beispiel "Obst"), schreiben wir: 
 
obst = new string[7]; 
 
Da der Arbeitsspeicher in zwei Teile geteilt ist, nämlich zum Einen der Stack und zum Zweiten der Heap und strings Verweise vom Stack auf den Heap sind, haben wir so 7 Verweise auf Speicherbereiche im Heap angelegt, in denen dann die Zeichenketten gespeichert werden. Hätten wir einen int oder einen anderen primitiven Datentyp außer einen String gewählt, so wäre gleich der benötigte Speicher berechnet und auf dem Stack belegt worden. 
So, nun wollen wir den im Array enthaltenen Variablen auch Werte zuweisen. Dazu gibt zwei Möglichkeiten.  
Schauen wir uns erst die einfachere Variante an. 
Dabei greifen wir über einen Index auf das gewünschte Objekt im Array zu. Der Index ist Nullbasiert, was bedeutet, dass man anfängt bei 0 und nicht etwa bei 1 zu zählen. Um also das erste Objekt im Array zu setzen schreiben wir folgenden Code: 
 
obst[0] = "Apfel"; 
 
Dieser Code "pickt" sich das erste Element im Array raus. Mit dem Zuweisungsoperator und dem entsprechenden Inhalt, den wir zuweisen (hier "Apfel") setzen wir so den ersten im Array enthaltenen string. Das kann man jetzt bis zum Index 6 in gleicher Weise weiterführen, wie hier gezeigt: 
 
obst[1] = "Birne"; 
obst[2] = "Banane"; 
obst[3] = "Pflaume"; 
obst[4] = "Himbeere"; 
obst[5] = "Orange"; 
obst[6] = "Kiwi"; 
 
Somit hätten wir das Array mit einigen Obstarten gefüllt.  
Versucht man das Array mittels der Methode Console.WriteLine(); auszugeben, so erhält man, anders als von den meisten von Euch wahrscheinlich erwartet wurde, nicht den Inhalt des Arrays auf die Konsole geschrieben sondern den Typ des Arrays. In dem obigen Beispiel ist das System.String[], da es sich ja um ein string-Array handelt. Wie man den Inhalt ausgibt schauen wir uns gleich an. Dabei wird auch die im letzten Tutorial erwähnte foreach-Schleife zum Einsatz kommen. 
Es gibt, wie oben schon erwähnt, noch eine zweite Möglichkeit, das Array zu initialisieren. Ich schreibe hier erst den Code und erkläre ihn dann: 
 
string[] obst = { "Banane", "Kiwi", "Apfel", "Orange", "Himbeere", "Birne", "Pflaume" }; 
 
Hierbei wird, wie vorher auch schon, eine Array-Variable vom Typ string[] erzeugt. Diese bekommt ebenfalls einen Namen. Der einzige Unterschied liegt hier in der Initialisierung des Arrays, denn hierbei wird kein Array mit festgelegter Anzahl an Variablen festgelegt, sondern es werden im altbekannten eckigen Klammerpaar einfach die gewünschten Werte, durch Kommata getrennt, angegeben. Daraus "bastelt" sich der Compiler selber ein Array zusammen, welches genauso aussieht wie das obige. 
 
Mehrdimensionale Arrays 
 
Bevor wir uns anschauen, wie man Arrays ausgibt, wollen wir eine weitere Array-Art betrachten; die mehrdimensionalen Arrays. Diese kommen vor allem bei der 3D-Spieleentwicklung häufiger vor. Der Name sagt eigentlich schon alles aus, trotzdem erkläre ich das hier noch mal. 
Die bis jetzt von uns erstellten Arrays waren eindimensional. Damit man sich das besser vorstellen kann, habe ich die folgende Tabelle erstellt. 
 
0 
1 
2 
3 
4 
5 
6 
 
Jede Spalte stellt hier eine Variable innerhalb des Arrays dar, jede Zahl den dazugehörigen Index. Ein zweidimensionales Array hat nicht nur Spalten, sondern auch Zeilen. Auch das kann man sich wieder am besten in Tabellenform veranschaulichen. 
 
0,0 
1,0 
2,0 
3,0 
4,0 
5,0 
6,0 
0,1 
1,1 
2,1 
3,1 
4,1 
5,1 
6,1 
0,2 
1,2 
2,2 
3,2 
4,2 
5,2 
6,2 
0,3 
1,3 
2,3 
3,3 
4,3 
5,3 
6,3 
0,4 
1,4 
2,4 
3,4 
4,4 
5,4 
6,4 
 
Man sieht hier schon auf den ersten Blick, dass die Indices anders aussehen als oben; sie bestehen immer aus 2 Zahlen. Dabei ähnelt die Angabe der, wenn man in einem 2-dimensionalen Koordinatensystem einen Punkt beschreiben will, und genau deshalb tragen mehrdimensionale Arrays diesen Namen.  
Genauso wie oben geht es weiter mit 3D-Arrays. Da dieses etwas schwer zu zeigen ist, lasse ich die grafische Darstellung. Ein Index eines 3D-Arrays hat dann eben 3 Zahlen, also z. B. 3,2,4. Natürlich sind auch alle anderen Arrays (z. B. 100D-Arrays) möglich. Diese kann man sich allerdings schwer vorstellen und finden auch fast keine Verwendung. 
Das Erstellen eines mehrdimensionalen Arrays sieht natürlich etwas anders aus. Für jede Dimension, die zusätzlich zur ersten hinzukommt, muss in den eckigen Klammern ein Komma hinzugefügt werden. Im folgenden Code-Beispiel wird ein 3D-Array erzeugt: 
 
int[,,] dreiD; 
 
Natürlich werden auch hier Indices benötigt, um auf Variablen innerhalb des Array zuzugreifen. Im folgenden wird ein 2D-Arry mit 3*4 Variablen erstellt und die Werte derselben gesetzt: 
 
string[,] zweiD = new string[3, 4]; 
zweiD[0, 0] = "0,0"; 
zweiD[0, 1] = "0, 1"; 
zweiD[0, 2] = "0, 2"; 
zweiD[0, 3] = "0, 3"; 
zweiD[1, 0] = "1, 0"; 
zweiD[1, 1] = "1, 1"; 
zweiD[1, 2] = "1, 2"; 
zweiD[1, 3] = "1, 3"; 
zweiD[2, 0] = "2, 0"; 
zweiD[2, 1] = "2, 1"; 
zweiD[2, 2] = "2, 2"; 
zweiD[2, 3] = "2, 3"; 
 
Auch hier gilt es wieder, den nullbasierte Index zu beachten! 
Natürlich ist auch bei mehrdimensionalen Arrays die zweite Art der Wertezuweisung möglich. Für jede Dimension wird hier ein Eckige-Klammern-Paar zusätzlich zu dem alles umgebenden benötigt, innerhalb des Klammernpaars kommen dann die Werte, durch Kommata getrennt. Die einzelnen Klammernpaare werden ebenfalls mit Kommata getrennt. Der Beispielcode: 
 
string[,] zweiD = { { "0, 0", "0, 1", "0, 2", "0, 3" }, { "1, 0", "1, 1", "1, 2", "1,3" }, { "2, 0", "2, 1", "2, 2", "2, 3" } }; 
 
Ausgabe von einzelnen Werten 
 
Wie oben erwähnt gibt die Anweisung Console.WriteLine(zweiD); nicht den Inhalt des Arrays aus (in diesem Fall "zweiD"), sondern nur den Typ (hier: System.String[,]). Um alle Werte der im Array enthaltenen Variablen auszugeben, ohne die Ausgabe für jeden einzelnen zu schreiben, braucht man die foreach-Schleife, die wir uns gleich anschauen.  
Um den Inhalt einer einzelnen Variablen des Arrays auszugeben, greift man einfach durch den Index auf die Variable zu, deren Inhalt dann von der Methode Console.WriteLine(); genauso dargestellt wird, als wäre es eine normale Variable (was ja eigentlich auch stimmt). Beispiel gefällig? 
Console.WriteLine(zweiD[1, 2]); 
 
Die Ausgabe lautet hierbei 1, 2 
 
Arrays in Verbindung mit Schleifen 
 
Nun endlich kommen wir zu den Arrays in Verbindung mit Schleifen, denn so kommen Arrays am häufigsten zum Einsatz.  
Zuerst schauen wir uns an, wie man den Inhalt eines Arrays mit Hilfe einer for-Schleife setzen und auslesen kann, danach, wie man alle Variablen innerhalb eines Arrays mit der foreach-Schleife durchlaufen kann. 
Die for-Schleife wird sehr häufig dazu benutzt, um den Inhalt eines Arrays zu setzen. Dabei können Formeln in Kombination mit der Iterationsvariable (siehe dieser Post) beispielsweise die Werte für kompliziertere Rechnungen setzen. Wir werden allerdings ein einfacheres Beispiel nehmen, und zwar wollen wir einfache in paar Namen generieren (genau 100 Stück). Wie immer erst der Code, dann die Erklärung: 
 
string[] namen = new string[100]; 
 
for (int i = 0; i < 100; i++) 
{ 
namen[i] = "Vorname " + (i + 1); 
} 
 
Zuerst wird ein string[]-Array mit dem Namen "namen" erstellt, und zwar mit Platz für 100 strings 
In der for-Schleife wird dann die altbekannte Iterationsvariable namens "i" erstellt. Diese bekommt den Wert 0, da ja der Index auch nullbasiert ist.  
Danach wird die Abbruchbedingung der Schleife gesetzt. Durch die hier gesetzte Abbruchbedingung läuft die Schleife nur so lange, wie i kleiner als 100 ist, das heißt 100 mal, weil wir ja von 0 anfangen zu zählen.  
Durch i++ wird bei jedem Schleifendurchlauf der Wert von i um jeweils 1 erhöht. 
In der Schleife greifen wir auf die Variable des Arrays am Index i zu. Da dieser sich ja mit jedem Schleifendurchlauf verändert und die Schleife von 0 bis 99 durchläuft, was den Indices des Arrays entspricht, bekommen wir so jede Variable mal dran. Der Wert einer jeden Variable bekommt das Suffix "Vorname ". Dahinter kommt der Wert der Iterationsvariable zuzüglich 1. Dieser Ausdruck (also i + 1) muss unbedingt in Klammern gesetzt werden, damit die Berechnung hier Vorrang vor dem Hinzufügen des Ergebnisses zum string, der dem Namen am Index i zugewiesen wird, erhält. Wir addieren hier die 1 hinzu, da man in der realen Welt normalerweise von 1 und nich von 0 aus zählt. 
Mit diesem Code bekommen wir 100 Namen automatisch generiert. Diese reichen von "Vorname 1" bis "Vorname 100". 
Um diese auszugeben haben wir jetzt 2 Möglichkeiten: Die for-Schleife oder die foreach-Schleife. Wir schauen uns zuerst wieder die Variante mit der for-Schleife an. 
Der Code: 
 
for (int i = 0; i < 100; i++) 
{ 
Console.WriteLine(namen[i]); 
} 
 
Eigentlich müsste er sich selbst erklären, da er ähnlich funktioniert wie oben, beim Zuweisen der Werte.  
Die Schleife ist exakt gleich, weshalb ich sie nicht erklären werde. Der Methode wird der Name am Index i übergeben. So werden nacheinander alle Namen ausgegeben, sodass das "Ergebnis" ungefähr so aussieht: 
 
Vorname 1 
Vorname 2 
 
Vorname 99 
Vorname 100 
 
Um mehrdimensionale Arrays mit Hilfe von for-Schleifen zu initialisieren verschachtelt man diese einfach und übergibt dann die Iterationsvariablen der beiden Schleifen als Indices. Ein kleines Beispiel: 
 
int[,] zahlen = new int[100, 100]; 
 
for (int i = 0; i < 100; i++) 
{ 
for (int j = 0; j < 100; j++) 
{ 
zahlen[i, j] = i * j; 
} 
} 
 
Hier wird erst ein zweidimensionales Array mit 100 * 100 Variablen vom Typ int erstellt. 
In zwei verschachtelten for-Schleifen werden dann alle Variablen des Arrays durchlaufen und diesen dann Werte zugewiesen. Dabei steht die Schleife mit der Iterationsvariable i für die eine Richtung (z. B. die Spalten, wenn man sich wieder die Tabelle vorstellen möchte) und die Schleife mit der Iterationsvariable j für die jeweils andere Richtung. 
Achtung: Wird ein Index außerhalb des Wertebereiches des Arrays aufgerufen (im obigen Beispiel beispielsweise der Index 100 oder 101), so wird eine Exception geworfen. Die Exception ist vom Typ "System.IndexOutOfRangeException" und beinhaltet die Information "Der Index war außerhalb des Arraybereichs". Dieser Fehler tritt gerne bei for-Schleifen auf, wenn nicht beachtet wurde, dass der Index Nullbasiert ist.
Nun schauen wir uns die versprochene Alternative an: Die foreach-Schleife. Auch hier erst der Code, bezogen auf das gerade verwendete Beispiel (die Ausgabe aller Namen): 
 
foreach(string name in namen) 
{ 
Console.WriteLine(name); 
} 
 
Wie bei allen anderen Schleifen stehen auch bei der foreach-Schleife runde Klammern hinter dem Schlüsselwort foreach. Hier wird wieder die Schleifenlogik implementiert.  
In den Klammern wird erst eine Variable vom Typ string und mit dem Namen "name" erstellt. Danach folgt das Schlüsselwort "in" und dann das Array, welches die Namen enthält (hier "namen"). 
Doch was verbirgt sich dahinter?  
Bei jedem Schleifendurchlauf wird der Variablen name der Wert der nächsten Variable des Arrays zugewiesen. Im ersten Schleifendurchlauf hat die Variable "name" also den Wert "Vorname 1", im nächsten "Vorname 2" und so fort). Das Schlüsselwort "in" mit dem nachfolgenden Array zeigt dabei an, aus welchem Array die Variablen durchlaufen werden sollen. Die Schleife erkennt dabei selber, wie viele Schleifendurchläufe benötigt werden, um alle Variablen des Arrays zu durchlaufen, wodurch die Gefahr einer möglicherweise auftretenden IndexOutOfRangeException abgeschafft wird. Der Nachteil der Schleife besteht darin, dass man nicht innerhalb der Schleife prüfen/wissen kann, in welchem Schleifendurchlauf (z. B. im 50sten) man sich befindet. 
 
Mit Console.WriteLine(name); wird dann der aktuelle Wert der "name"-Variable ausgegeben. 
 
So, das war es für heute mit den Arrays. Ich hoffe, dass euch der Post gefallen hat und natürlich, dass ihr alles verstanden habt. Falls Fragen offen bleiben gibt es ja die Kommentare, MSDN, Galileo OpenBook und Foren.
Bis dahin, 
Julian