Letztes Update: 15.11.2017
Behandelte Befehle: Arraydefinition, Arrayzugriff, length

In diesem Kapitel lernen wir, wie man viele Informationen in einem Konstrukt abspeichert. Zum Beispiel wollen wir in einem Spiel mehrere 10 Bälle fliegen lassen und wollen für alle Bälle die x-Koordinate speichern. Wir wollen aber nicht 10 verschiedene Variablen anlegen, weil wir später vielleicht entscheiden, dass wir 100 Bälle brauchen. Oder wir wollen während des Spiels die Anzahl der Bälle verändern. Alles ganz leicht mit Arrays.

8.1 Einführung in Arrays

Video: Arrays (10:38)

Array erzeugen

Wenn eine Variable sowas wie ein Becher ist, dann ist ein Array sowas wie ein Becherhalter. Nehmen wir eine Integer-Variable für die x-Position eines Balls oder auch eine Float-Variable oder eine boolesche Variable.

int x;
float foo = 42.3;
boolean play = true;

Die Variable x ist nur deklariert, wird aber automatisch mit dem Wert Null gefüllt. Das kann man sich so vorstellen:

Jetzt möchten wir einen Array anlegen, der z.B. gleich fünf x-Positionen speichern kann. Das machen wir in zwei Schritten. Zunächst deklarieren wir eine Variable, die einen Becherhalter enthalten kann. Man beachte, dass der Becherhalter an sich noch nicht existiert!

int[] xarray; // Arrayvariable deklarieren

Ein Array ist ein Objekt (wie PVector oder String). Die Variable xarray kann einen Array speichern, ist aber jetzt noch leer. Wie wir wissen, setzt Processing/Java daher eine Markierung namens null ein:

Jetzt müssen wir noch den Becherhalter erzeugen. Hier müssen wir uns festlegen, wie viele Becher genau das Array halten kann. Stellen Sie sich das Array wie ein Muffin-Backblech vor, da müssen Sie sich bei Herstellung auch entscheiden, wieviele Muffin-Löcher Sie reinstanzen.

Wir nehmen mal 5 Becher:

xarray = new int[5]; // Array mit 5 Elementen erzeugen

Häufig macht man beides in einem Schritt:

int[] xarray = new int[5];

Unseren Becherhalter kann man sich wie ein Gitter vorstellen, eine Batterie von Behältern. Die Variable xarray zeigt jetzt auf dieses Gebilde:

Beachten Sie, dass der Datentyp dieser Variable int[] ist, also ein Integer-Array, angedeutet durch die eckigen Klammern. Die Variable xarray kann also nicht etwa eine einzelne Zahl speichern, sondern eben nur diesen kuriosen Becherhalter.

Elemente zugreifen

Jetzt haben Sie plötzlich 5 Becher statt nur einem. Wir nennen die Becher auch Array-Elemente. Wie kommen Sie an so ein Element ran? Ganz einfach: die Elemente sind durchnummeriert, und zwar von 0 bis 4. Diese Zahlen nennt man auch Indices bzw. im Singular Index.

Diese fünf Elemente können Sie mit folgender Syntax abrufen:

println(xarray[0]); // Element 0 ausgeben
println(xarray[1]); // Element 1 ausgeben
println(xarray[2]);
println(xarray[3]);
println(xarray[4]); // letztes Element ausgeben

Zu Beginn haben Sie da überall Nullen drin, weil jeder einzelne Becher vom Typ Integer ist. Wenn Sie das ändern wollen, schreiben Sie zum Beispiel:

xarray[3] = 42; // Zuweisung

Intern sieht das Array jetzt so aus:

Wenn Sie genau hinschauen, werden Sie feststellen, dass sich ein einzelnes Element wie xarray[0] ganz genau so verhält wie eine handelsübliche Integer-Variable. Man muss sich nur an diese eckigen Klammern gewöhnen.

Sie können z.B. auch mit Array-Elementen rechnen:

int result = xarray[0] + xarray[5] - 10;

Größe des Array

Wir haben oben ein Array mit 10 Elementen geschaffen. Diese Zahl ist eine Eigenschaft des Array-Objekts, die wir per Punktnotation abrufen können und zwar mit xarray.length .

Beispiel:

int[] xarray = new int[555];
println(xarray.length);
555

Beliebter Fehler (Exception)

Der beliebteste Anfängerfehler bei Arrays ist es, nicht zu beachten, dass Informatiker immer bei Null anfangen zu zählen. Ist ja auch merkwürdig. Versuchen Sie mal:

int[] xarray = new int[10];

xarray[10] = 23; // Index 10 existiert nicht!

Processing sagt zu recht: ArrayIndexOutOfBoundsException. Eine 'Exception' (engl. Ausnahme) bedeutet beim Programmieren 'Fehler'. In diesem Fall ist der Fehler, dass das Element 10 nicht existiert. Es existieren nur Elemente mit Namen 0 bis 9.

Kurzschreibweise: Array erzeugen und befüllen

Wenn Sie schnell ein Array erzeugen und gleich mit konkreten Werten befüllen möchten, schreiben Sie:

int[] zahlen = {8, 42, -3, 200};

Dieses Konstrukt erzeugt eine neue Array-Variable zahlen der Länge 4 mit den entsprechenden Werten. Die ausführliche Variante wäre:

int[] zahlen = new int[4];
zahlen[0] = 8;
zahlen[1] = 42;
zahlen[2] = -3;
zahlen[3] = 200;

Vorsicht: Die Kurzschreibweise dürfen Sie nur dann verwenden, wenn die Variable deklariert wird, also ganz am Anfang der Lebensdauer einer Array-Variablen. Die Kurzschreibweise ist später nicht mehr erlaubt. Das hier geht nicht:

int[] zahlen = new int[3];
zahlen = { 5, 20, 8 }; // FEHLER! Nur bei Deklaration erlaubt.

Array ausgeben

In Processing können Sie übrigens mit

println(zahlen);

sehr bequem die Inhalte eines Arrays betrachten, die jeweilige Indexzahl wird in Klammern mit ausgegeben:

[0] 8
[1] 42
[2] -3
[3] 200

Diese besondere Art der Ausgabe gibt es nur in Processing, nicht in Java.

Coding Style

Bitte achten Sie darauf, wo Leerzeichen und Zeilenumbrüche gemacht werden, und wo nicht.

int[] zahlen;

zahlen = new int[10];

zahlen[2] = zahlen[0] + zahlen[1];

Denken Sie auch daran, Ihre Code mit Bearbeiten > Autoformatierung von Processing korrekt einrücken zu lassen (Tastenkürzel STRG+T bzw. CMD+T).

Übungsaufgaben

(a) Arrays erstellen

Erstellen Sie je einen Array für

  • 5 ganze Zahlen
  • 3 Strings
  • 4 Kommazahlen
  • 3 boolesche Werte

Befüllen Sie anschließend die Arrays mit beliebigen Werten und geben Sie einige Werte einzeln aus, d.h. für ein einzelnes Element eine Print-Anweisung.

Probieren Sie anschließend auch folgende Befehle (foo sei ein Array, aber kein String-Array):

println(foo);
print(foo);
Tipp
Erklärung
Normalerweise kann man einen Array nicht einfach ausdrucken, da hier ja mehrere Werte vorliegen. Man müsste also eigentlich mit einer Schleife die Einzelwerte abrufen und mit print() ausgeben. Processing ist aber so freundlich, auch einen kompletten Array bei println() zu akzeptieren und zeigt Ihnen dann alle Wert und den jeweiligen Index. Das funktioniert allerdings nur bei println(), nicht bei print(). Bei String-Arrays haben Sie nochmal eine andere Darstellung, probieren Sie es mal.

(b) Arrays verwenden 1

Erstellen Sie einen int-Array namens foo mit den Zahlen 5, 45, -10, 20 (in dieser Reihenfolge). Addieren Sie die ersten zwei Zahlen und legen Sie das Ergebnis in einer neuen int-Variablen a ab. Addieren Sie die letzten zwei Zahlen und legen Sie das Ergebnis einer neuen int-Variablen b ab.

Printen sie a und b auf die Konsole und überprüfen Sie die Werte.

(c) Arrays verwenden 2

Erstellen Sie einen int-Array namens bar mit den Zahlen 2, 4, 6 (in dieser Reihenfolge).

Multiplizieren Sie die ersten zwei Zahlen und legen Sie sie das Ergebnis in der dritten Zelle des Arrays ab. Sie sollten also {2, 4, 8} in bar haben. Schreiben Sie bar mit println() auf die Konsole, um dies zu prüfen.

Zusammenfassung

Ein Array kann mehrere Dinge vom gleichen Typ speichern. Man kann sich einen Array als Schrank mit durchnummerierten Fächern vorstellen.

  • Der Datentyp eines Array ist der Typ der enthaltenen Element und dahinter eckige Klammern, z.B. int[] für einen Integer-Array oder String[] für einen String-Array
  • Array mit 5 Fächern erzeugen: new int[5]
  • Elemente sind durchnummeriert, beginnend bei Null. Diese Nummer nennt man Index eines Elements.
  • Zugriff auf ein Element über den Index: foo[3] greift auf das 4. Element zu
  • Länge des Array foo mit foo.length.
  • Schnelles Erzeugen und Befüllen eines Array: int[] foo = { 3, 42, -50 };

8.2 Arrays mit Schleifen

Bislang scheinen Arrays nur eine umständliche Art zu sein, schnell viele Variablen zu erzeugen. Ihre volle Kraft entfalten Arrays aber erst im Zusammenspiel mit Schleifen.

Jetzt mal ein praktisches Beispiel: Wir lassen drei Bälle über den Bildschirm fliegen.

int[] xarray = new int[3]; // erzeugen

void setup() {
  // initialisieren
  xarray[0] = 0;
  xarray[1] = 20;
  xarray[2] = 40;
}

void draw() {
  background(255);

  // Werte verwenden
  ellipse(xarray[0], 50, 10, 10);
  ellipse(xarray[1], 50, 10, 10);
  ellipse(xarray[2], 50, 10, 10);

  // Werte hochzählen
  xarray[0]++;
  xarray[1]++;
  xarray[2]++;
}

Sie haben jetzt drei Bälle und ein Array mit drei Elementen. Sie hätten genauso schreiben können:

int x1;
int x2;
int x3;

void setup() {
  x1 = 0;
  x2 = 20;
  x3 = 40;
}

void draw() {
  background(255);
  ellipse(x1, 50, 10, 10);
  ellipse(x2, 50, 10, 10);
  ellipse(x3, 50, 10, 10);
  x1++;
  x2++;
  x3++;
}

Der Vorteil der Array-Lösung? Sie können die einzelnen Elemente mit Hilfe einer Schleife durchlaufen. Nehmen wir an, Sie wollen ein Array mit lauter 5en füllen:

int[] fuenfer = new int[10];

Das müsste man so machen:

fuenfer[0] = 5;
fuenfer[1] = 5;
fuenfer[2] = 5;
fuenfer[3] = 5;
fuenfer[4] = 5;
fuenfer[5] = 5;
fuenfer[6] = 5;
fuenfer[7] = 5;
fuenfer[8] = 5;
fuenfer[9] = 5;

Wir sehen aber: alles bleibt gleich, bis auf den Index (die Zahl in Klammern). Also könnten wir diese Zahl durch eine Laufvariable in einer Schleife ersetzen:

for (int i = 0; i < 10; i++) {
  fuenfer[i] = 5;
}

Sehen wir uns die Entwicklung der Dinge in der Schleife an. Es wird die Laufvariable i rundenweise hochgezählt. Bevor der Code ausgeführt wird, wird noch die Schleifenbedingung i < 10 geprüft. Ist diese false, ist die Schleife fertig.

In unserem Ball-Beispiel von oben kann man zwei Schleifen einfügen. In der ersten Schleife wird die x-Startposition mit i * 20 berechnet. In drei Durchläufen ergibt sich jeweils: 0 * 20, 1 * 20 und 2 * 20, also 0, 20, 40.

int[] xarray = new int[3];

void setup() {
  for (int i = 0; i < 3; i++) {
    xarray[i] = i * 20; // berechne x-Startposition
  }
}

void draw() {
  background(255);
  for (int i = 0; i < 3; i++) {
    ellipse(xarray[i], 50, 10, 10); // zeichnen
    xarray[i]++; // hochzählen
  }
}

Diese Lösung ist nicht nur kürzer, sondern auch mächtiger. Sie können nämlich mit einem Fingerschnipp die Anzahl der Bälle erhöhen, z.B. auf 5. Dazu müssen Sie an drei Stellen im Code die 3 durch 5 ersetzen.

Größe des Arrays verwenden

Die Lösung oben lässt sich verbessern, indem man in die For-Schleife die Größe des Arrays mit xarray.length einsetzt, zum Beispiel in der unteren Schleife:

for (int i = 0; i < xarray.length; i++) {
  ellipse(xarray[i], 50, 10, 10); // zeichnen
  xarray[i]++; // hochzählen
}

Wenn Sie dies für beide Schleifen tun, dann müssen Sie nur noch eine Stelle ändern, um die Anzahl der Bälle anzupassen: ganz oben.

// optimale Lösung
int[] xarray = new int[3];

void setup() {
  for (int i = 0; i < xarray.length; i++) {
    xarray[i] = i * 20;
  }
}

void draw() {
  background(255);
  for (int i = 0; i < xarray.length; i++) {
    ellipse(xarray[i], 50, 10, 10);
    xarray[i]++;
  }
}

Probieren Sie es aus: setzen Sie bei int[3] oben eine andere Zahl ein. Versuchen Sie 2 oder 10 oder nur eine 1. Alles kein Problem.

Übungsaufgaben

In den folgenden Aufgaben sollte immer eine For-Schleife zum Einsatz kommen.

(a) Befüllen

Erstellen Sie einen Array für 10 ganze Zahlen und befüllen Sie die Elemente mit 1, 2, ..., 10.

Ihr Programm soll so allgemein geschrieben sein, dass Sie auch Arrays mit 20 oder 100 Elementen befüllen können.

Varianten: Befüllen Sie den Array mit

  • -10, -9, ..., -2, -1
  • 2, 4, 6, ..., 20

(b) Befüllen à la Fibonacci

Befüllen Sie einen Array der Länge 10 mit den Fibonacci-Zahlen: 1, 1, 2, 3, 5, 8, 13, ...

Beachten Sie, dass diese Variante etwas anders funktioniert (einfacher!) als die Aufgabe, mit Hilfe einer Schleife Fibonacci-Zahlen zu erzeugen. Hier können Sie auf die Vorgängerzahlen im Array zugreifen!

Tipp
Besetzen Sie die ersten zwei Elemente im Array direkt mit je 1 und starten Sie in der Schleife ab dem dritten Element (Index 2).

(c) Befüllen von zwei Arrays

Befüllen Sie zwei Arrays gleichzeitig in einer Schleife. Beide Arrays haben Länge 10. Das eine Array wird mit 1, 2, 3, ... befüllt, das zweite Array mit -10, -9, -8, ...

(d) Elemente verdoppeln

Verändern Sie den Array dahingehend, dass jedes Element verdoppelt wird.

int[] bla  = { 2, 1, 3, 5 };

// Hier Code schreiben

println(bla);

Sie sollten das hier auf der Konsole sehen:

[0] 4
[1] 2
[2] 6
[3] 10

(e) Summe

Berechnen Sie die Summe von allen Elementen des Array und geben Sie diese auf der Konsole aus. Der Code sollte ohne weitere Änderungen funktionieren, wenn Sie Zahlen zu foo hinzufügen oder entfernen.

int[] foo = { 22, -10, 8, 10 };

// Hier Code schreiben

Sie sollten das hier auf der Konsole sehen:

30
Tipp
Verwenden Sie eine neue int-Variable, um die Summe schrittweise "einzusammeln".

(f) Array filtern

Gegeben sei Array a. Drucken Sie alle negativen Elemente untereinander auf der Konsole aus.

int[] a  = { 1, -2, -25, 6, -3, 5 };
Tipp
Sie benötigen ein if.

(g) Mehrere Arrays addieren

Gegeben sind drei Arrays a, b und c. Verändern Sie Array c dahingehend, dass im Element c[0] die Summe a[0] und b[0] steht und entsprechend für die Elemente 1, 2, 3, ...

int[] a  = { 1, 2, 25, 6 };
int[] b  = { 9, 18, 5, 34 };
int[] c = new int[4];

// Hier Code schreiben

println(c);

Sie sollten das hier auf der Konsole sehen:

[0] 10
[1] 20
[2] 30
[3] 40

(h) Arrays zusammenfügen

Gegeben sind zwei Arrays a und b. Erzeugen Sie einen neuen Array c, der so lang ist wie a und b zusammengenommen und auch die Werte von a und b (in dieser Reihenfolge) enthält.

int[] a  = { 1, 2, 25 };
int[] b  = { 9, 18 };

// Hier Code schreiben

println(c);

Sie sollten das hier auf der Konsole sehen:

[0] 1
[1] 2
[2] 25
[3] 9
[4] 18

Verwenden Sie nicht die Processing-Funktionen concat() oder splice(). Ihr Code sollte auch funktionieren, wenn Sie bei a und/oder b Elemente hinzufügen oder entfernen.

Tipp

Zerlegen Sie das Problem in zwei Schritte. In Schritt 1 befüllen Sie den ersten Teil von c mit den Werten von a. In Schritt 2 füllen Sie den zweiten Teil von c mit den Werten von b.

Jeden Schritt lösen Sie mit einer eigenen For-Schleife. Schritt 1 ist leicht, denn die Indexzahlen von a und c sind gleich (c[0] bekommt Wert von a[0] usw.). Schritt 2 erfordert eine kleine Änderung. Wie lautet die Indexzahl in c für den ersten Wert von b?

(i) Array rückwärts

Gegeben sei ein Array:

int[] foo = { 1, 2, 3, 4 };

Erstellen Sie einen neuen Array bar, wo die Elemente in umgekehrter Reihenfolge abgelegt sind. Wenn Sie bar ausgeben, sehen Sie also:

[0] 4
[1] 3
[2] 2
[3] 1

Verwenden Sie nicht die Processing-Funktion reverse(). Wie immer soll Ihr Code auch für andere Arrays funktionieren.

Tipp
Sie laufen mit einer For-Schleife und Laufvariablen i durch 0, 1, 2, 3 und befüllen den neuen Array bar. Welchen Wert weisen Sie bar[i] zu? Notieren Sie das per Hand. Es geht um einen Zusammenhang zwischen den Indexzahlen von bar und foo.

(j) Elemente verschieben

Gegeben sei ein Array:

int[] foo = { 10, 20, 30, 40, 50 };

Verschieben Sie alle Elemente ab Index 1 um eine Stelle nach rechts. Als Ergebnis sollte hinter das folgende in foo stehen:

[0] 10
[1] 10
[2] 20
[3] 30
[4] 40
Tipp
Wenn Sie Schwierigkeiten haben (z.B. weil Ihr Array hinterher 10 an allen Stellen enthält), dann probieren Sie zunächst die Aufgabe, alle Element nach links zu schieben.

(k) Nachbarn addieren

Gegeben sei ein Array mit einer geraden Anzahl von Elementen:

int[] foo = { 1, 2, 3, 4, 5, 6 };

Erstellen Sie einen neuen Array bar, das halb so lang ist wie foo und wo jeweils zwei benachbarte Elemente von foo addiert wurden, also hier 1+2 und 3+4 und 5+6. Wenn Sie bar ausgeben, sehen Sie also:

[0] 3
[1] 7
[2] 11

(l) Boolesche Operationen *

Abstrakt geht es um die Frage, wie Sie bei einer Menge von N Wahrheitswerten herausfinden, ob mindestens ein Wert true ist (entspricht ODER) bzw. ob alle Werte true sind (entspricht UND). Die Werte liegen in einem Array vor.

Zur Vorbereitung der Aufgabe erzeugen Sie einen Array "infiziert" mit zehn Elementen. Jeder Eintrag soll mit einer Wahrscheinlichkeit von 5% true sein.

boolean[] infiziert = new boolean[10];

for (int i = 0; i < infiziert.length; i++) {
  // Ihr Code
}
Tipp
Verwenden Sie random(100). Sie erhalten einen zufälligen Wert zwischen 0 und 100.

Die erste Aufgabe ist jetzt festzustellen, ob mindestens eine/r infiziert ist von den 10. Sie sollen dies mit Hilfe einer Schleife lösen, damit Ihre Lösung auch für sehr große Arrays funktioniert. Geben Sie sowohl den Array als auch Ihre Lösung aus, um zu sehen, ob Ihr Programm korrekt arbeitet.

Tipp
Verwenden Sie eine boolesche Variable. Welchen Wert hat diese zu Beginn, was passiert damit in der Schleife?

Die zweite Aufgabe ist festzustellen, ob alle infiziert sind. Um dies zu testen, setzen Sie die Wahrscheinlichkeit auf 95%.

Zusammenfassung

Arrays und For-Schleifen sind die perfekten Partner. Mit einer For-Schleife können Sie ganz leicht einen kompletten Array befüllen (z.B. alle Elemente mit der Zahl 0 oder mit einer Zufallszahl).

int[] xarray = new int[3];

for (int i = 0; i < 3; i++) {
  xarray[i] = 0;
}

Außerdem können Sie mit einer Schleife alle Elemente eines Arrays abrufen, z.B. um Dinge zu zeichnen (wenn ein Array etwa die x-Koordinate enthält).

for (int i = 0; i < 3; i++) {
  ellipse(xarray[i], 50, 10, 10);
}

Arrays sind eigentlich Objekte. Mit der Objekteigenschaft length können Sie die Länge eines Arrays herausfinden. Das nutzen wir in allen For-Schleifen, um den Code möglichst flexibel zu halten (d.h. der Code funktioniert auch dann, wenn die Länge sich ändert).

for (int i = 0; i < xarray.length; i++) {
  ellipse(xarray[i], 50, 10, 10);
}

8.3 Eigenschaften in Arrays speichern

Personen repräsentieren

Stellen Sie sich vor, Sie möchten mehrere Dinge speichern, zum Beispiel eine Person, aber diese Dinge haben mehrere Eigenschaften. Eine Person hat einen Namen, ein Alter, ein Geschlecht. In einem Array können Sie aber nur eine Eigenschaft speichern.

Die Lösung ist, für jede Eigenschaft ein eigenes Array zu verwenden. Ein String-Array für die Namen der Personen und ein int-Array für die Altersangaben:

String[] namen;
int[] alter;

Jetzt können wir drei Personen speichern:

String[] namen = { "Max Schmidt", "Lisa Marx", "Robin Meier" };
int[] alter = { 33, 28, 12 };

Das Bindeglied zwischen den Arrays ist die Indexzahl:

  • Index 0 steht für "Max Schmidt" und für Alter 33
  • Index 1 für "Lisa Marx" und Alter 28
  • Index 2 für "Robin Meier" und Alter 12
Jetzt wollen wir noch das Geschlecht repräsentieren. Wir nutzen einen Array von booleans, wobei true weiblich bedeutet:

String[] namen = { "Max Schmidt", "Lisa Marx", "Robin Meier" };
int[] alter = { 33, 28, 12 };
boolean[] weiblich = { false, true, false };

Wie können wir diese Darstellung nutzen? Wir könnten alle Personen mit allen Eigenschaften ausdrucken. Dabei benutzen wir die Information zum Geschlecht, um die richtige Anrede zu wählen.

for (int i = 0; i < namen.length; i++) {
  if (weiblich[i]) {
    print("Frau ");
  } else {
    print("Herr ");
  }
  println(namen[i] + ", " + alter[i] + " Jahre alt");
}

Die Ausgabe ist:

Herr Max Schmidt, 33 Jahre alt
Frau Lisa Marx, 28 Jahre alt
Herr Robin Meier, 12 Jahre alt

Grafische Objekte repräsentieren

Arrays bieten sich auch an, um mehrere grafische Objekte und deren Eigenschaften zu speichern. Wollen wir die x/y-Koordinaten von Rechtecken speichern, verwenden wir zwei Arrays xpos und ypos. Für uns ist klar: für jeden Index gibt es ein Rechteck und die beiden Elemente, z.B. xpos[3] und ypos[3] beinhalten die Koordinaten des 4. Rechtecks (nicht die Null vergessen!).

// 100 Rechtecke mit je zwei Eigenschaften (xpos, ypos),
// die über die Cursortasten verändert werden können

int[] xpos = new int[100];
int[] ypos = new int[100];

void setup() {
  for (int i = 0; i < xpos.length; i++) {
    xpos[i] = (int)random(0, width);
    ypos[i] = (int)random(0, height);
  }
}

void draw() {
  background(0);
  fill(255);
  for (int i = 0; i < xpos.length; i++) {
    rect(xpos[i], ypos[i], 10, 10);
  }
}

void keyPressed() {
  if (keyCode == LEFT) {
    for (int i = 0; i < xpos.length; i++) {
      xpos[i]--;
    }
  }
  if (keyCode == RIGHT) {
    for (int i = 0; i < xpos.length; i++) {
      xpos[i]++;
    }
  }
  if (key == ' ') {
    for (int i = 0; i < xpos.length; i++) {
      xpos[i] = (int)random(0, width);
      ypos[i] = (int)random(0, height);
    }
  }
}

Interaktives Feld:(erst auf das graue Feld klicken, dann Cursortasten)

Im oberen Beispiel haben wir 100 Objekte mit je zwei Eigenschaften (xpos und ypos). Jetzt nehmen wir eine dritte Eigenschaft hinzu: die Farbe.

// 100 Rechtecke mit je drei Eigenschaften
// Die Cursortasten ändern jetzt die jeweilige Graustufe

int[] xpos = new int[100];
int[] ypos = new int[100];
int[] farbe = new int[100];

void setup() {
  for (int i = 0; i < xpos.length; i++) {
    xpos[i] = (int)random(0, width);
    ypos[i] = (int)random(0, height);
    farbe[i] = (int)random(0, 256);
  }
}

void draw() {
  background(0);
  for (int i = 0; i < xpos.length; i++) {
    fill(farbe[i]);
    rect(xpos[i], ypos[i], 10, 10);
  }
}

void keyPressed() {
  if (keyCode == LEFT) {
    for (int i = 0; i < xpos.length; i++) {
      farbe[i] -= 5;
      farbe[i] = constrain(farbe[i], 0, 255);
    }
  }
  if (keyCode == RIGHT) {
    for (int i = 0; i < xpos.length; i++) {
      farbe[i] += 5;
      farbe[i] = constrain(farbe[i], 0, 255);
    }
  }
}

Die Farbe muss mit der Funktion constrain() im Wertebereich 0..255 gehalten werden.

Übungsaufgaben

Die Aufgaben (b) - (e) bauen aufeinander auf.

(a) Personen filtern

Sie haben eine Anzahl Personen wie oben beschrieben in drei Arrays repräsentiert:

String[] namen = {
  "Max Schmidt", "Lisa Marx", "Robin Meier",
  "Lara Huber", "Anna Groß", "Heribert Lehmann",
  "Harry Potter", "Obi-wan Kenobi", "Julia Kron"
};
int[] alter = {
  33, 28, 12,
  22, 23, 64,
  16, 102, 18
};
boolean[] weiblich = {
  false, true, false,
  true, true, false,
  false, false, true
};

Listen Sie mit Hilfe einer Schleife nur die Frauen auf und zwar wie folgt:

Frau Lisa Marx
Frau Lara Huber
Frau Anna Groß
Frau Julia Kron

Jetzt sollen Sie zählen! Verwenden Sie eine Schleife und eine Zählervariable, um folgende Personengruppen zu zählen:

  • Alle Frauen
  • Alle Personen unter 40
  • Alle Männer über 30

Geben Sie die drei Zahlen auf der Konsole aus.

(b) Viele Bälle

Lassen Sie 10 Bälle in unterschiedlichen Geschwindigkeiten (zufällig) über den Bildschirm fliegen. Geben Sie jedem Ball eine eigene Farbe.

Verwenden Sie random() in setup(), um die x/y-Koordinaten zufällig zu setzen. Das gleiche gilt für die Geschwindigkeit.

(c) Ausschalten *

Bauen Sie ein, dass man jeden einzelnen Ball "ausschalten" kann. Verwenden Sie einen Array von booleans. Bauen Sie ein, dass man über die Zifferntasten 0, ..., 9 die entsprechenden Bälle ein- und ausschalten kann. (Sie können die Bälle entweder ganz verschwinden lassen oder auch nur die Bewegung rausnehmen, d.h. der Ball bleibt stehen.)

Tipp
Jeder Buchstabe und jedes Zeichen hat einen eindeutigen "Zeichencode", eine ganze Zahl. In der Systemvariablen keyCode steht immer der Code der zuletzt gedrückten Taste. Finden Sie zunächst heraus, welche Codes die Tasten 1, 2, 3 etc. haben, indem Sie sich den Code mit println() ausgeben lassen.

(d) Killerball

Führen Sie einen neuen Ball ein, der nicht im Array ist und den Sie mit den Cursortasten steuern können. Sobald dieser Spielball einen der anderen Bälle berührt, verschwindet der andere Ball.

Wie Sie die Kollision erkennen, steht in Kap. 5.6.

(e) Gewinnen

Wenn alle Bälle fort sind, gewinnen Sie. In diesem Fall setzen Sie ein "Gewonnen!" direkt auf die Screen (vor schwarzem Hintergrund).

Sie sollten das Ein-/Ausschlalten der Bälle über einen Array von Booleans regeln. Um zu testen, ob alle Elemente false sind, verwenden Sie eine Schleife. Siehe auch die letzte Aufgabe in 8.1.

Tipp

Führen Sie eine neue boolesche Variable (z.B. gameOver) ein, die Sie in draw() benutzen, um zu unterscheiden, ob Sie das "Spiel" darstellen oder den Text "Gewonnen". Testen Sie, indem Sie "per Hand" die Variable auf true/false setzen.

Schreiben Sie eine Funktion checkGewinn(), die Sie immer am Ende von draw() aufrufen. In checkGewinn() untersuchen Sie Ihren Array von Boolean (s.o.) und setzen entsprechend gameOver.

Zusammenfassung

Ein Array kann eine Serie von Daten eines Typs speichern, z.B. ganze Zahlen oder Strings.

Wenn Sie Daten für eine Reihe von Entitäten haben, seien es Personen oder grafische Objekte, können Sie die verschiedenen Aspekte dieser Entitäten (Name, Alter, Koordinaten etc.) in verschiedenen Arrays speichern. Der Zusammenhalt dieser Daten wird durch die Indexzahl gewährleistet: eine Indexzahl repräsentiert eine Entität (Person oder Objekt).

Wenn wir zum Beispiel drei Personen mit Name und Alter speichern wollen, können wir das mit zwei Arrays tun:

String[] namen = { "Max Schmidt", "Lisa Marx", "Robin Meier" };
int[] alter = { 33, 28, 12 };

Zum Ausgeben der Personen verwenden Sie eine Schleife:

for (int i = 0; i < namen.length; i++) {
  println(namen[i] + ", " + alter[i] + " Jahre");
}

8.4 Objekte in Arrays speichern

Natürlich können Sie auch Objekte wie Vektoren oder Bilder in Arrays speichern. Dazu verwenden Sie einfach den Klassennamen (PVector) als Typ und versehen ihn mit eckigen Klammern.

PVector[] spaceships = new PVector[5];

Wichtig ist hier, dass das Array noch keine Objekte enthält. Es wurde noch kein new verwendet, also existieren auch keine Objekte im Speicher.

Schauen Sie sich z.B. das Element 0 als ganzes an, sehen Sie auch das "null":

println(spaceships[0]);
null

Sollten Sie auf die Idee kommen, einen Wert abgreifen zu wollen, werden Sie mit der Fehlermeldung NullPointerException bestraft:

println(spaceships[0].x); // NullPointerException

Das heißt, Sie müssen erst einmal durchs komplette Array laufen und Objekte erzeugen. Zum Beispiel mit (0,0)-Vektoren:

for (int i = 0; i < spaceships.length; i++) {
  spaceships[i] = new PVector();
}

Jetzt haben Sie den Speicher mit Objekten bevölkert und in jede Arrayzelle eine Referenz hinterlegt.

Sie können natürlich auch Zufallswerte verwenden:

for (int i = 0; i < spaceships.length; i++) {
  float x = random(100);
  float y = random(100);
  spaceships[i] = new PVector(x, y);
}

Auch der Zugriff auf die einzelnen Zellen des Arrays findet über die eckigen Klammern und den Index statt:

for (int i = 0; i < spaceships.length; i++) {
  rect(spaceships[i].x, spaceships[i].y, 20, 20);
}

Übungsaufgaben

(0) Fingerübung 1

Erstellen Sie einen Array mit fünf Vektor-Objekten. Die fünf Vektoren sollen die Werte (1,1), (2,2) usw. haben.

Verwenden Sie eine Schleife.

(0) Fingerübung 2

Erstellen Sie einen Array von fünf Vektor-Objekten mit Werten (10, 10), (10, 30), (10, 50), (10, 70) und (10, 90).

Zeichnen Sie Kreise mit Durchmesser 20 an den fünf Koordinaten.

Verwenden Sie zwei Schleifen.

(a) Bälle

Lassen Sie viele Bälle fliegen. Verwenden Sie dazu zwei Arrays: position und speed. Die Arrayelemente sollen vom Typ PVector sein.

Schauen Sie im Kapitel über Objekte nach, wie Position und Speed addiert werden.

(b) Teleprompter

Speichern Sie einen Text wortweise in einem Array von Strings. Auf dem Grafikbildschirm sieht man das erste Wort.

String[] woerter = { "Ich", "bin", "ein", "Text"};

Wenn man auf die Cursor-rechts-Taste drückt, erscheint das nächste Wort. Wenn man auf die Cursor-links-Taste drückt, erscheint das vorige Wort. Achten Sie darauf, dass Sie nicht "aus dem Array rauslaufen" (Processing wird sich mit einem Fehler zu Wort melden).

(c) Fliegende Bilder

Statt Kreisen lassen Sie jetzt Bilder fliegen, z.B. Asteroiden, Strandbälle, Angry Birds...

Verwenden Sie dazu den Code aus (a) und fügen Sie einen Array von PImage-Objekten hinzu.

Tipp
Wenn Sie die Bilder schematisch benennen, z.B. bild1.jpg, bild2.jpg, bild3.jpg usw. können Sie das Anlegen der PImage-Objekte ökonomisch mit einer Schleife regeln.

8.5 Flexible Arrays

Arrays haben einen großen Nachteil: Die Länge muss zu Beginn festgelegt werden. Jetzt stellen Sie sich vor, Sie möchten während des Programmlaufs neue "Objekte" zu Ihrem Array hinzufügen (man stelle sich vor: ein Adressbuch und neue Adressen oder ein Spiel mit laufend neuen Asteroiden und sonstigen Objekten). Wie macht man das?

Die Grundidee ist: Wir erzeugen einen Array, der wesentlich größer ist, als wir zunächst brauchen, und verwenden nur eine Teil davon. Nehmen wir an, wir möchten uns Punkte merken. Dann speichern wir x- und y-Koordinate in zwei Arrays. Zu Beginn möchten wir uns nur zwei Punkte merken, aber wir erzeugen dennoch Arrays der Länge 100:

int[] punktX = new int[100];
int[] punktY = new int[100];

void setup() {
  punktX[0] = 20;
  punktY[0] = 20;

  punktX[1] = 40;
  punktY[1] = 40;

  punktX[2] = 60;
  punktY[2] = 60;
}

Da wir nicht wissen, wie viele Elemente dieser zwei Arrays wirklich "sinnvoll" verwendet werden, können wir keine Schleife definieren, um die sinnvoll gefüllten Elemente für das Zeichnen zu verwenden. Die Lösung ist, eine neue Variable einzuführen, die angibt, wie weit das Array befüllt ist. In unserem Fall wäre die "Länge" gleich 3.

int[] punktX = new int[100];
int[] punktY = new int[100];
int num = 3;

Jetzt können wir eine Zeichenroutine einführen, die auf num basiert:

void draw() {
  for (int i= 0; i < num; i++) {
    ellipse(punktX[i], punktY[i], 10, 10);
  }
}

Was ist damit gewonnen? Sie können zum Beispiel per Mausklick neue Kreise hinzufügen:

int[] punktX = new int[100];
int[] punktY = new int[100];
int num = 3;

void setup() {
  punktX[0] = 20;
  punktY[0] = 20;

  punktX[1] = 40;
  punktY[1] = 40;

  punktX[2] = 60;
  punktY[2] = 60;
}

void draw() {
  for (int i= 0; i < num; i++) {
    ellipse(punktX[i], punktY[i], 10, 10);
  }
}

void mousePressed() {
  punktX[num] = int(random(0, width));
  punktY[num] = int(random(0, height));
  num++;
}

Zu beachten ist, dass num immer eins höher ist als der aktuell verwendete Index, da es sich um die Länge unseres verwendeten Bereichs im Array handelt. Außerdem für der obige Code zu einem Fehler, wenn mehr als 100 Punkte erreicht sind. Wie können Sie verhindern, dass der Fehler auftritt?

Sie können übrigens auch Bälle wieder entfernen. Dazu müssen Sie lediglich num um eins verringern. Probieren Sie es doch einmal aus, z.B. indem Sie beim Druck auf eine Tastaturtaste einen Ball entfernen. Auch hier lauert ein Fehler! Wann bekommen Sie hier ein Problem und wie verhindern Sie es?

Übungsaufgabe

(a) Punkte und Linien

Schreiben Sie ein Programm, wo bei jedem Mausklick ein Punkt an der Stelle der aktuellen Mausposition erzeugt wird. Beim nächsten Klick wird wieder ein Punkt erzeugt und eine Linie zwischen den Punkten gezogen.

Interaktives Feld:(mit der Maus in das Feld klicken)

Hinweis: Lösen Sie die Aufgabe mit Hilfe von flexiblen Arrays wie oben beschrieben (für jeweils x und y). Sie können alternativ auch einen Array von PVector-Objekten benutzen.

Zusatzaufgabe 1: Wenn Sie auf die Backspace-Taste (oder eine andere) drücken, soll der zuletzt erzeugte Punkt wieder verschwinden.

Zusatzaufgabe 2: Erweitern Sie Ihren Code so, dass Sie mit Hilfe der Cursortasten die gesamten gezeichnete Figur pixelweise nach oben/unten/rechts/links verschieben können.

(b) Punkte löschen

Erweitern Sie das obere Programm wie folgt: Wenn Sie auf einen bereits gezeichneten Punkt klicken, dann soll dieser Punkt verschwinden. Die Linie verbinden sich anschließend mit den beiden Nachbarpunkten.

Dazu müssen Sie die Teilaufgabe lösen, wie Sie aus einem Array ein beliebiges Element löschen - denn an der Stelle des gelöschten Elements darf natürlich keine "Lücke" bleiben.

(c) Skalieren

Binden Sie die die +/- Tasten an eine Zoomfunktion. Wenn Sie auf + drücken, vergrößert sich das Gesamtgebilde. Der Ausgangspunkt für die Vergrößerung ist der Bildschirmmittelpunkt. Sie benötigen dafür ein wenig Vektorrechnung (müssen aber nicht notwendigerweise PVector-Objekte verwenden).