Java-Kurs: Arrays
Willemers Informatik-Ecke
Schleifen Methoden
Arrays ermöglichen die Zusammenstellung gleichartiger Variablen zu einem Verbund, in dem jedes Element über seine Position zugreifbar ist. Ein typisches Beispiel sind die Lottozahlen. Diese sind ein Verbund aus sechs ganze Zahlen. Also spielen wir ein wenig Lotto.

Würde man die Lottozahlen in sechs Variablen ablegen, ergäben sich folgende Aufgaben:

Anlegen eines Arrays

Ein Array wird durch die eckigen Klammern spezifiziert. Eine Variable ist ein Array von int, wenn vor oder hinter der Variablen ein leeres Paar eckige Klammern definiert sind.
int [] lotto;
Die Variable enthält nicht selbst den Speicher des Arrays, sondern verweist nur auf den Speicher. In Java wird eine solche Variable eine Referenz genannt. Sie enthält nicht die Daten, sondern den Verweis auf Daten, die sich im Speicher befinden.

Noch weiß die Variable allerdings nichts von Speicher. Der muss mit dem Befehl new erst angelegt werden. Auf den Befehl folgt der Typ der Elementvariablen und in eckigen Klammern, wie viele Elemente benötigt werden. Im Falle von Lotto wird int verwendet und es sind 6 Zahlen.

int [] lotto = new int[6];
Sollte bei einem Zugriff auf ein Array eine NullPointerException auftreten, haben Sie mit großer Wahrscheinlichkeit vergessen, den new-Befehl anzuwenden.

Damit existieren im Speicher sechs Speicherplätze für int, die über die Referenz lotto zugegriffen werden. Um ein einzelnes Feld zuzugreifen, wird hinter lotto ein rechteckiges Klammernpaar mit der Positionsnummer verwendet. Dabei ist zu berücksichtigen, dass die Zählung bei 0 beginnt und da es sechs Elemente sind, die höchste Position die Nummer 5 ist.

a[0] = 15;   // Erstes Element wird mit 15 besetzt
a[5] = 48;   // Letztes Element wird mit 48 besetzt
int zahl = a[5]; // Auslesen des letzten Elements
Sollen alle Elemente des Lottofeldes mit Werten gefüllt werden, drängt sich die for-Schleife förmlich auf.
final int MAX = 6;
int [] lotto = new int[MAX];
for (int i=0; i<MAX; i++) {
   lotto[i] = 1;
}

Spezial-for für das Auslesen eines Arrays

Java verfügt über eine spezielle Variante der for-Schleife, mit der Arrays ausgelesen werden können. Dabei wird im Schleifenkopf einer Variablen des gleichen Typs wie einer Elementvariable nacheinander jedes Element der Schleife zugewiesen.
final int MAX = 6;
int [] lotto = new int[MAX];
for (int zahl : lotto) {
    System.out.println(zahl);
}

Die Größe eines Arrays auslesen

Alle Arrays besitzen eine Instanzvariable namens length, mit der man deren Länge ermitteln kann.
System.out.println(lotto.length);

Lottozahlen erstellen

Lottospielen macht viel mehr Spaß, wenn man die Zahlen selbst zieht. Dazu müssen die Zahlen mit Werten belegt werden, so dass es keine doppelten Zahlen gibt und anschließend sortiert werden.

Zufall

Hier müssen wir einen kleinen Ausflug in die Zufallszahlenerzeugung machen. Die Bibliothek java.util stellt eine Klasse Random zur Verfügung. Von dieser muss ein Objekt mit dem Befehl new erzeugt werden. Darüber kann die Methode nextInt aufgerufen werden, die eine zufällige ganze Zahl liefert, die zwischen 0 und dem übergebenen Wert liegt. Da die Lottozahlen bei 1 beginnen, addieren wir eine 1 auf den Wert.
java.util.Random zufall = new java.util.Random();
int[] lotto = new int[6];
for (int i=0; i<6; i++) {
    lotto[i] = zufall.nextInt(48) + 1;
}

Doppelte vermeiden

Ärgerlicherweise kann es passieren, dass zufällig zwei gleiche Zahlen gezogen werden. Man könnte dies anschließend prüfen und dann eine neue Ziehung veranlassen. Das ist aber wenig effizient. Sinnvoller ist es, bei jeder gezogenen Zahl zu prüfen, ob diese Zahl in einer der vorigen Runden gezogen worden ist und dann nur diese eine Zahl noch einmal zu ziehen.

Dazu wird in die Schleife eine weitere Schleife gebettet, die einen neuen Zähler immer von der 0 bis zur bisher letzten gezogenen Zahl läuft und darin die aktuell gezogene mit den vorigen Zahlen vergleicht.

Gibt es eine Gleichheit, wird der Index der äußeren Schleife herabgesetzt. Damit wird erreicht, dass die letzte Zahl noch einmal gezogen wird.

java.util.Random zufall = new java.util.Random();
int[] lotto = new int[6];
for (int i=0; i<6; i++) {
    lotto[i] = zufall.nextInt(48) + 1;
    for (int j=0; j<i; j++) {
        if (lotto[i]==lotto[j]) {
            // Doppelt! Letzte Zahl noch einmal ziehen
            j = 6; // innere Schleife abbrechen
            i--;   // Ziehung wiederholen
        }
    }
}

Sortieren

Nun liegen die Lottozahlen unsortiert vor. Die wohl einfachste Form des Sortierens ist es, von links nach rechts durchzugehen und zu vergleichen, ob die linke kleiner als die rechte ist. Ist das nicht der Fall, werden die beiden getauscht.

for (int i = 0; i < 5; i++) {
    if (lotto[i] > lotto[i + 1]) {
        int tausch = lotto[i];
        lotto[i] = lotto[i + 1];
        lotto[i + 1] = tausch;
    }
}

Leider reicht ein einziger Durchlauf nicht. Es wird zwar besser, aber noch nicht vollständig sortiert. Ein Durchlauf gewährleistet aber immerhin, dass das höchste Element anschließend ganz rechts ist. Versuchen wir es damit, dass wir die Schleife sechs Mal durchlaufen.

for (int j = 0; j < 6; j++) {
    for (int i = 0; i < 5; i++) {
        if (lotto[i] > lotto[i + 1]) {
            int tausch = lotto[i];
            lotto[i] = lotto[i + 1];
            lotto[i + 1] = tausch;
        }
    }
}

Ist das optimal? Bestimmt nicht. Als erstes erinnern wir uns, dass bei einem inneren Durchlauf die größte Zahl immer nach rechts außen wandert. Dann müssen wir die rechteste Zahl nicht mehr vergleichen.

In der zweiten Runde muss man die rechten beiden Werte nicht mehr vergleichen, in der dritten Runde die rechten drei und so weiter.

Wir erreichen das, indem wir die Endbedingung der inneren Schleife von konstant 5 auf 5-j setzen.

for (int j = 0; j < 6; j++) {
    for (int i = 0; i < 5-j; i++) {
        if (lotto[i] > lotto[i + 1]) { 
            int tausch = lotto[i]; 
            lotto[i] = lotto[i + 1];
            lotto[i + 1] = tausch;
        }
    }   
}       

In der sechsten Runde wird die innere Schleife gar nicht mehr durchstarten, da 5-5 ja 0 ergibt. Also reduzieren wir die Grenze der äußeren Schleife von 6 auf 5.

for (int j = 0; j < 5; j++) {
    for (int i = 0; i < 5-j; i++) {
        if (lotto[i] > lotto[i + 1]) {
            int tausch = lotto[i];
            lotto[i] = lotto[i + 1];
            lotto[i + 1] = tausch;
        }
    }
}

Bibliotheksaufruf

Die einfachere Variante ist es, die Sortierfunktion aus der Bibliothek java.util zu verwenden, die für Arrays zur Verfügung steht.
java.util.Arrays.sort(lotto);

Array-Literale

Sie können auch ohne new arbeiten, wenn Sie das Array mit einem Literal vorbelegen. Ein Literal für ein Array besteht aus dessen Elementliteralen, die kommasepariert in einem geschweiften Klammernpaar stehen. Falls Ihnen bestimmte Lottozahlen besonders gut gefallen, könnten Sie sie so anlegen:
int [] lotto = { 1, 4, 9, 16, 25, 36 };
Natürlich müssen es nicht zwingend ganze Zahlen sein. Das folgende Beispiel zeigt, wie ein Kartenspiel simuliert wird. Dazu wird ein Array von 32 int-Werten angelegt, die für ein Skatblatt stehen. Gefüllt wird es mit den Zahlen von 0 bis 31.

Teilt man eine solche Zahl durch 4, erhält man einen Wert zwischen 0 und 7, den man den Kartenwerten 7, 8, 9, 10, B, D, K und A zuordnen kann. Verwendet man die Modulo-Rechnung erhält man einen Wert zwischen 0 und 3, der jeweils einer Kartenfarbe zugeordnet werden kann.

Um diese Karten benutzerfreundlich anzuzeigen, kann man jeweils ein Array von char anlegen, dass die Symbole enthält. Leider ist die 10 zweistellig, darum verwenden wir hier ein Z. Da Kreuz und Karo beide mit K anfangen, verwenden wir Treff statt Kreuz, das mit T anfängt.

public class Kartenspiel {
    public static void main(String[] args) {
        int[] blatt = new int[32];
        char[] farbe = {'T', 'P', 'H', 'K' };
        char[] wert = {'A', 'K', 'D', 'B', 'Z', '9', '8', '7'};
        for (int i=0; i<32; i++) {
            blatt[i] = i;
        }
        for (int karte : blatt) {
            System.out.print(farbe [karte%4]);
            System.out.print(wert[karte/4]);
            System.out.print(" ");
        }
    }
}
Wenn Sie das Programm ausführen erhalten Sie folgende Ausgabe:
TA PA HA KA TK PK HK KK TD PD HD KD TB PB HB KB
TZ PZ HZ KZ T9 P9 H9 K9 T8 P8 H8 K8 T7 P7 H7 K7
Sie können nun das Skatblatt mischen und auf drei 10-er Arrays verteilen, diese wieder sortieren und haben den Beginn eines kleinen Skatprogramms.

Mehrdimensional

Arrays können auch mehrdimensional sein, hier am Beispiel eines Spielfelds. Vor Spielbeginn soll das Spielfeld mit Pluszeichen gefüllt werden. Während des Spiels soll ein Spieler Felder ändern können. Hier wird es simuliert, indem die 4. Position von oben und die 6. Position von links mit einem X markiert wird.
char[][] spielfeld = new char[7][9];
for (int i=0; i<7; i++) {
    for (int j=0; j<9; j++) {
        spielfeld[i][j] = '+';
    }
}
spielfeld[3][5] = 'X';
for (char[]zeile : spielfeld) {
    for (char feld : zeile) {
        System.out.print(" "+feld);
    }
    System.out.println();
}
Die Ausgabe des Programms sieht folgendermaßen aus:
 + + + + + + + + +
 + + + + + + + + +
 + + + + + + + + +
 + + + + + X + + +
 + + + + + + + + +
 + + + + + + + + +
 + + + + + + + + +

Referenz und ihre Konsequenz

Eine Array-Variable enthält nicht die Daten des Arrays, sondern nur eine Referenz auf die Daten. Das hat Konsequenzen:

Videos

Teil 1: Anlegen und Umgang mit einem Array

Teil 2: Kopieren von Arrays

Teil 3: Lottozahlen mit Zufall

Teil 4: Mehrdimensionale Arrays


Schleifen Methoden