Home > Informatik > Stufe EF > Folge 7 (Arrays)

7.1 Einfache Arrays, Teil 1

Arrays - Objekt-Arrays - verstehen - Held

Einführendes Beispiel

Eine Java-Methode soll die kleinere von zwei int-Zahlen zurückliefern. Hier eine spontane Lösungsidee:

Programm 1
   public int mini(int a, int b) 
   {
      if (a < b)
         return a;
      else
         return b;
   }

Wenn der erste Parameter kleiner ist als der zweite, wird er als die "kleinste Zahl" interpretiert und zurückgeliefert. Ist der erste Parameter nicht kleiner als der zweite, wird der zweite Parameter als "kleinste Zahl" zurückgeliefert, selbst dann, wenn er genau so groß ist wie der erste Parameter.

Nun wollen wir die Methode so erweitern, dass das Minimum von drei Zahlen zurückgeliefert wird.

Programm 2
   public int mini(int a, int b, int c) 
   {
      if ((a < b) && (a < c))
         return a;
      else
         if (b < c)
            return b;
         else
            return c;
   }

Zunächst wird geschaut, ob a die kleinste der drei Zahlen ist. Wenn das nicht der Fall ist, muss b oder c die kleinste Zahl sein. Dazu wird einfach b mit c verglichen und je nach Ergebnis b oder c als Wert zurückgegeben.

Für Experten

Hier könnte eine Expertin nun einwenden: "Wir haben doch eben eine mini()-Methode geschrieben, die das Minimum von zwei Zahlen zurückliefert. Könnte man diese Methode nicht in der neuen mini()-Methode benutzen, um den Quelltext kürzer zu gestalten?"

Gute Idee, das probieren wir doch gleich mal aus - obwohl das ja mit dem neuen Thema "Arrays" gar nichts zu tun hat. Aber Informatik ist eine experimentelle Naturwissenschaft und sollte auch etwas Spaß machen. Und wenn man eine so gute Idee hat, sollte man die auch gleich ausprobieren:

    public int mini(int a, int b) 
    {
      if (a < b)
         return a;
      else
         return b;
    }
    
    public int mini(int a, int b, int c) 
    {
      if ((a < b) && (a < c))
         return a;
      else
         return mini(b,c);
    }

Tatsächlich funktioniert dieses Vorgehen, allerdings ist der Quelltext insgesamt nicht kürzer geworden, weil wir jetzt ja zwei dieser mini()-Methoden haben.

Mal eine ganz nebensächliche Frage:
Wieso funktioniert dieser Quelltext eigentlich? Müsste sich die zweite Methode mini() nicht dauernd selbst aufrufen (Rekursion!)?

Es gibt tatsächlich zwei verschiedene mini()-Methoden. Die erste dieser Methoden hat zwei int-Parameter, die zweite Methode hat aber drei int-Parameter. Daher lassen sich die beiden Methoden leicht unterscheiden, obwohl sie den gleichen Namen haben. Es kommt also nicht zu einer Rekursion.

Zurück zum eigentlichen Thema. Wir sind immer noch bei der Einführung zum Thema "Arrays".

Wenn wir die Methode auf vier Parameter erweitern, wird der Quelltext noch länger und unübersichtlicher, über fünf oder sechs Parameter wollen wir gar nicht mehr nachdenken.

Der Sprung ins kalte Wasser!

Der Sprung ins kalte Wasser
Quelle: Pixabay.com, Autor: chengtzf, Lizenz: Pixabay-Lizenz.

Genug mit den hilflosen Versuchen, das Minimum von 4, 5, 6 oder mehr Zahlen zu ermitteln. Wir wollen jetzt das Minimum von 100 Zahlen ermitteln - und das mit einem sehr kurzen Quelltext. Dazu benutzen wir einen Array. Bevor wir hier mit langen und langweiligen Erklärungen anfangen, schauen wir uns einfach mal ein Beispiel an. Erklärungen kommen dann später.

import java.util.Random;

public class ArrayEinfuehrung
{
    int[] hundertZahlen;
    
    public ArrayEinfuehrung()
    {
       hundertZahlen = new int[100];
    }
    
    public void erzeugeArray()
    {
        Random wuerfel = new Random();
        for (int i=0; i<100; i++)
           hundertZahlen[i] = wuerfel.nextInt(1000)+1;
    }
    
    public int mini() 
    {
       int min = hundertZahlen[0];
       for (int i=1; i<100; i++)
          if (hundertZahlen[i] < min)
             min = hundertZahlen[i];
             
       return min;
    }
}

Das ist schon ein recht langes Programm. Kopieren Sie es sich über die Zwischenablage in ein neues BlueJ-Projekt und probieren Sie es aus. Es funktioniert einwandfrei. Aber leider können wir die 100 erzeugten Zufallszahlen nicht sehen, und so können wir auch nicht überprüfen, ob die Methode mini() tatsächlich die kleinste der 100 Zufallszahlen zurückliefert. Aber dazu kommen wir noch später, bei den Übungen. Jetzt wollen wir uns das Programm erst einmal näher anschauen.

import java.util.Random;

Mit dieser ersten Anweisung teilen wir dem Programm mit, dass wir einen Zufallsgenerator aus der Klasse Random benötigen.

int[] hundertZahlen;

Mit dieser Zeile teilen wir dem Programm mit, dass wir Speicherplatz für mehrere bis viele int-Zahlen benötigen. Es wird aber noch nicht gesagt, wie viele Zahlen im Arbeitsspeicher untergebracht werden sollen. Der Bezeichner für den Zugriff auf diese Zahlen wird aber schon hier festgelegt: hundertZahlen.

hundertZahlen = new int[100];

Jetzt sagen wir dem Programm, dass wir Speicherplatz für genau 100 int-Zahlen benötigen.

 Random wuerfel = new Random();

Mit dieser Zeile erzeugen wir ein Objekt wuerfel der Klasse Random. Dieses Objekt ist jetzt unser Zufallsgenerator.

        
for (int i=0; i<100; i++)
    hundertZahlen[i] = wuerfel.nextInt(1000)+1;

Diese for-Schleife weist jeder der 100 Zahlen eine Zufallszahl im Bereich zwischen 1 und 1000 zu, beispielsweise 364.

Die Laufvariable i dient zur Identifizierung einer bestimmten Zahl des Arrays, man bezeichnet eine solche Identifikationsnummer als Index. Jedes Arrayelement hat einen bestimmten Index. Hat der Array 100 Elemente, so hat das erste Element den Index 0 (und nicht etwa den Index 1). Das letzte der 100 Elemente hat folglich nicht den Index 100, sondern den Index 99. Diese Eigenart der Arrays muss man sich gut einprägen, sonst sind Programmierfehler vorprogrammiert.

 int min = hundertZahlen[0];

Jetzt befinden wir uns schon in der Methode mini(). In der ersten Zeile dieser Methode "behaupten" wir einfach mal so, dass das erste Element des Arrays (Index = 0) das kleinste Element im Array ist. Wir weisen der lokalen Variable min also den Wert des ersten Arrayelementes zu.

for (int i=1; i<100; i++)
    if (hundertZahlen[i] < min)
        min = hundertZahlen[i];

Jetzt kommt die typische Strategie zum Durchsuchen eines Arrays. Mit Hilfe einer weiteren for-Schleife schauen wir uns jedes Arrayelement näher an. Wenn das Arrayelement mit dem Index i kleiner sein sollte als der Wert, der in min gespeichert ist, dann wird der Wert von min einfach auf den Wert dieses Arrayelementes gesetzt.

Angenommen, der kleinste bisher gefundene Wert ist 34 und wir sind gerade bei i = 12. Das Arrayelement mit dem Index 13 hat den Wert 56. Die Bedingung

if (hundertZahlen[13] < min) bzw. if (56 < 34)

ist nicht erfüllt, daher passiert nichts, und die Laufvariable wird auf den Wert i=14 gesetzt. Das Arrayelement hundertZahlen[14] hat jetzt beispielsweise den Wert 23. Die Bedingung

if (hundertZahlen[14] < min) bzw. if (23 < 34)

ist jetzt erfüllt. Dann kommt die entscheidende Zeile

min = hundertZahlen[i];

Die lokale Variable min wird jetzt also auf den Wert 23 gesetzt. Und so geht das immer weiter, bis das Ende des Arrays erreicht ist. Sobald ein Arrayelement gefunden wird, das kleiner ist als der aktuelle Wert von min, wird min wieder auf diesen noch kleineren Wert gesetzt.

return min;

Diese Zeile erklärt sich wohl von selbst. Der Wert von min wird von der Methode mini() zurückgegeben.

Was ist überhaupt ein Array?

Ein Array ist eine endliche Menge völlig gleichartiger Variablen. Unser Array aus dem obigen Programm besteht aus 100 int-Variablen. Genau so gut könnte man einen Array erzeugen, der aus 200 double-Variablen oder 2127 String-Variablen besteht.

Wie deklariert man einen Array?

Ein Array wird in einer Java-Klasse ähnlich wie ein normales Attribut deklariert. Vergleichen wir einmal die Deklaration einer einzelnen int-Variable und eines Arrays aus lauter int-Variablen:

int eineZahl;
int[] achtZahlen;

Der Array heißt zwar achtZahlen, die Arraygröße ist jedoch noch nicht festgelegt; das geschieht erst bei der Initialisierung des Arrays (s.u.). Auf die gleiche Weise könnte man auch einen Array aus double-Zahlen oder Strings deklarieren:

double[] zwanzigZahlen;
String[] textseite;
Was passiert bei der Deklaration?

Bei der Deklaration teilt man dem Rechner bzw. dem Java-System mit, dass man einen Array haben möchte, und wie der Array heißen soll. Nähere Angaben, vor allem wie viele Elemente der Array haben soll, hat man noch nicht gemacht.

Nach der Deklaration

int[] achtZahlen;

hat man zwar eine Variable erzeugt, die "achtZahlen" heißt, aber in der Variable steht noch nichts drin, sie ist leer.

Für Experten: Die Variable zeigt noch nicht auf einen konkreten Platz im Arbeitsspeicher.

Wie initialisiert man einen Array?

Bei der Initialisierung wird die Zahl der gewünschten Arrayelemente festgelegt.

achtZahlen = new int[8];

Offensichtlich will man hier acht int-Zahlen verwenden.

Für Experten: Die Variable achtZahlen zeigt jetzt auf einen bestimmten Bereich im Arbeitsspeicher, der so groß ist, dass genau acht int-Zahlen untergebracht werden können. Die erste Speicherzelle dieses Bereichs hat eine bestimmte Adresse, und diese Startadresse wird jetzt in der Variable achtZahlen gespeichert. Das kann man leicht ausprobieren.

public class KleinerTest
{
    int[] achtZahlen;
    
    public KleinerTest()
    {
       achtZahlen = new int[8];
       System.out.println(achtZahlen);
    }
}

Ausgegeben wird jetzt der Wert: [I@38638550. Das ist die Darstellung der Speicheradresse für den Array auf einem Rechner unter macOS. Bei einem Windows- oder Unix-Rechner wird es ähnlich aussehen.

Wie weist man einem Arrayelement Werte zu?
Ein Beispiel
achtZahlen[0] = 1;
achtZahlen[1] = 4;
achtZahlen[2] = 9;

Zur Zuweisung eines Wertes an ein Arrayelement benötigt man die Adresse dieses Elementes. Dazu dient der Index. Auf jedes Arrayelement kann direkt mithilfe des Index zugegriffen werden. Das erste Arrayelement hat immer den Index 0.

Weitere Beispiele mit ausführlichen Erläuterungen
zahl[0] = 13;

"zahl" ist hier der Bezeichner, 0 der Index, und 13 der Wert.

temperatur[12] = 27.3;

Hier heißt der Bezeichner "temperatur", der Index ist 12, und der zugewiesene Wert ist 27.3.

inventar[2] = new Gegenstand("Hammer",0,45,2);

Der Bezeichner heißt hier "inventar", der Index ist 2, und der Wert ist nichts anderes als ein neues Objekt der Klasse Gegenstand. Dieses neue Objekt wird in dieser Programmzeile erst erzeugt, das sieht man ja an dem Wort "new" und dem Aufruf des Konstruktors der Klasse Gegenstand.

for (int k=0; k<max; k++) liste[k] = k*k+3*k-2;

Hier haben wir ein schönes Beispiel, wie man mit Hilfe von for-Schleifen (oder anderen Schleifen) den Umgang mit Arrays erleichtern kann. Weitere Informationen dazu finden Sie auf der Spezialseite "Arrays und for-Schleifen".

Wie greift man auf den Wert von Arrayelementen zu?
Hier ein paar Beispiele
System.out.println(zahl[3]);

Ein Arrayelement kann mit dem println()-Befehl ausgegeben werden.

int ergebnis = zahl[2];

Den Wert eines Arrayelements kann man einer anderen Variablen zuweisen.

int summe = zahl[0] + zahl[1] + zahl[2];

Man kann mehrere Arrayelemente gleichzeitig auswerten.

if (zahl[1] < 0) 
			zahl[1] = 0;

Man kann innerhalb von if-Abfragen, while-Schleifen etc. auf den Wert bestimmter Arrayelemente zugreifen.

inventar[4].ausgeben();

In einem Objekt-Array kann auf die einzelnen Objekte zugegriffen werden, man kann sogar Methoden dieser Objekte aufrufen und ausführen lassen. Hier wird die ausgeben-Methode des fünften Elementes des Arrays inventar aufgerufen. Das ist möglich, weil jedes Arrayelement ein eigenes Objekt mit Attributen und Methoden ist.

Weitere Beispiele mit ausführlichen Erläuterungen
int y = zahl[0];

Das erste Arrayelement wird ausgelesen, der ausgelesene Wert wird dann an die int-Variable y überwiesen. Also hat y hinterher den gleichen Wert wie das erste Arrayelement.

System.out.println(temperatur[12]);

Das 13. Arrayelement (Index 12) wird ausgelesen, der ausgelesene Wert wird dann an den System.out.println()-Befehl übergeben und in das Konsolenfenster ausgegeben.

if (zahlenliste[4] >= 20)
   bedingung = true;
else
   bedingung = false;

Das fünfte Arrayelement wird ausgelesen und mit der Zahl 20 verglichen. Wenn der Wert dieses Arrayelementes größer oder gleich 20 ist, so wird die Variable bedingung - offenbar eine boolean-Variable - auf den Wert true gesetzt. Ist dies nicht der Fall, erhält bedingung den Wert false.

bedingung = (zahlenliste[4] >= 20);

Diese Anweisung macht exakt das Gleiche wie die vier Zeilen des vorherigen Beispiels. Der Ausdruck zahlenliste[4] >= 20 ist eine logische Bedingung, also entweder true oder false. Egal, welchen Wert diese Bedingung hat, das Ergebnis wird dann an die Variable bedingung überwiesen.

inventar[3].beschaedigen(20);

Der Array inventar speichert Objekte der Klasse Gegenstand - was man hier nicht gleich sieht. Jedes Objekt hat nicht nur Attribute, sondern auch Methoden. Diese Anweisung greift auf die Methode beschaedigen() des vierten Objektes des Arrays inventar zurück und ruft diese Methode mit dem Parameter 20 auf. Das vierte Objekt soll also um den Wert 20 beschädigt werden.

Arrays als Parameter von Methoden

Man kann Arrays als Attribute von Klassen verwenden. In unserem einführenden Beispiel mit den 100 Zahlen haben wir das auch gemacht. Dieses Vorgehen hat den Vorteil, dass alle Methoden der Klasse schreibend und lesend auf die Elemente des Arrays zugreifen können.

Manchmal will man aber auch einfach eine unabhängige und universell einsetzbare Methode schreiben, die eine Operation auf einen beliebigen int- oder double-Array ausführt (oder auf Arrays mit anderen Elementtypen). Wenn eine solche Methode wirklich unabhängig von einer bestimmten Klasse sein soll, darf man innerhalb der Methode nicht auf einen Array mit einem konkreten Namen zugreifen. Die Methode weiß ja noch gar nicht, wie der Array heißt, den sie bearbeiten oder auswerten soll. Außerdem soll die Methode auf viele verschiedene Arrays mit unterschiedlichen Namen zugreifen können. Erst dann ist die Methode universell als Werkzeug einsetzbar. Betrachten wir einmal, wie eine solche universelle Methode aufgebaut sein könnte:.

   public int mini(int[] zahlen)
   {
      int mini = zahlen[0];

      for (int i=1; i < zahlen.length; i++)
         if (zahlen[i] < mini)
            mini = zahlen[i];

      return mini;
   }

Das ist fast der gleiche Quelltext wie in unserem Einführungsbeispiel mit den 100 Zahlen. Allerdings heißt der Parameter jetzt nicht mehr "hundertZahlen", sondern einfach nur "zahlen", weil ja eine beliebige Anzahl von Arrayelementen übergeben werden kann. Das hängt ja davon ab, welche andere Methode mini() aufruft.

Die Deklaration des Parameters beginnt genau so wie die Deklaration eines Array-Attributs, nämlich mit der Nennung des Datentyps (hier int) und einem Paar eckiger Klammern, gefolgt von einem lokalen Namen, in unserem Beispiel zahlen.

Innerhalb der Methode kann dann auf die einzelnen Elemente des übergebenen Arrays lesend und schreibend zugegriffen werden, wie bereits weiter oben erklärt.

Seitenanfang -
Weiter mit "Einfache Arrays, Teil 2"...